带有 .bak _before_ 文件扩展名的备份文件

dot*_*hen 7 shell rename

我通常在没有版本控制的服务器上备份配置文件,如下所示:

# cp httpd.conf{,.bak}
# ls
httpd.conf    httpd.conf.bak
Run Code Online (Sandbox Code Playgroud)

但是,对于 web 根目录中的文件,我会花时间小心地将.bak文件扩展名放在文件扩展名之前,以便.php如果有人设法猜测或探测服务器以获取此类文件,则 Apache 仍将通过 PHP 解释器发送文件:

$ cp index.php index.bak.php
$ ls
index.php    index.bak.php
Run Code Online (Sandbox Code Playgroud)

有没有办法使用制表符完成和肌肉记忆将.bak文件扩展名放在前面?我已经Tab{}?,.bak陷入了肌肉记忆,我无法在 web 根目录中的文件上使用这个漂亮的“肉宏”。

谢谢。

Joh*_*han 7

可能很简单

cp file.{ext,bak.ext}
Run Code Online (Sandbox Code Playgroud)

bash {} 扩展扩展为所有组合(与列出现有文件的 [] 扩展不同。因此它生成两个文件名。

{ .... , .... } 提供了两个选项,因此您可以获得 file.ext 和 file.bak.ext

在您的脚本中,您将使用

cp $FILE.{$EXTENTION,bak.$EXTENTION}
Run Code Online (Sandbox Code Playgroud)

并将 FILENAME 分成两半,您可以使用

FILE=${FILENAME%.*}
EXTN=${FILENAME##*.}
Run Code Online (Sandbox Code Playgroud)

${VAR%xxxxx} 将返回 VAR 中内容的左侧直到匹配模式,在上面的情况下,模式是 .* 所以你得到除了最后一个 .* 的所有内容

${VAR##xxxxx} 将在匹配模式之后返回 VAR 中内容的右侧。双散列意味着获得最大可能的匹配,以防有多个可能的匹配。

要保存您的脚本,请检查在获取 FILE 和 EXTENTION 后是否存在不同的情况。在文件名没有扩展名的情况下会发生这种情况。

脚本的子部分可能如下所示:

ls *html | while read FILENAME
do
    FILE=${FILENAME%.*}
    EXTN=${FILENAME##$FILE}
    cp $FILE{$EXTN,.bak$EXTN}
done
Run Code Online (Sandbox Code Playgroud)

  • 那么为什么不只是 `cp file{,.bak}.ext` 呢? (7认同)

Sté*_*las 5

你总是可以使用一个函数:

backup()
  for i do
    case ${i##*/} in
      (?*.?*) cp -iv -- "$i" "${i%.*}.bak.${i##*.}";;
      (*) cp -iv -- "$i" "$i.bak"
    esac
  done
Run Code Online (Sandbox Code Playgroud)

${i##*/}是的基本名称$i(即$i一切到最右边的/移除),因为我们不想考虑bar/file作为扩展foo.bar/file

然后我们检查是否$i包含扩展名,即它是否包含至少一个dot字符,两边都有字符(?*任何一个字符后跟0 个或多个字符,所以它是一个或多个字符)。我们不能这样做*.*,否则我们最好的备份~/.bashrc作为~/.bak.bashrc例如。

上面的语法是 POSIX,除了-v(对于verbose)选项cp是 GNU 扩展,也可以在其他一些实现中找到,比如 BSD。

然后你可以做这样的事情:

backup foo.php bar.html baz<Tab>...
Run Code Online (Sandbox Code Playgroud)

或者:

backup *.php
Run Code Online (Sandbox Code Playgroud)

不必担心丢失数据(-i在覆盖文件之前会要求确认,另请参阅-bGNU 选项cp)或没有选择正确的名称。