Pat*_*ryk 173 shell-script arguments
我遇到了这个脚本:
#! /bin/bash                                                                                                                                                                                           
if (( $# < 3 )); then
  echo "$0 old_string new_string file [file...]"
  exit 0
else
  ostr="$1"; shift
  nstr="$1"; shift  
fi
echo "Replacing \"$ostr\" with \"$nstr\""
for file in $@; do
  if [ -f $file ]; then
    echo "Working with: $file"
    eval "sed 's/"$ostr"/"$nstr"/g' $file" > $file.tmp 
    mv $file.tmp $file
  fi  
done
Run Code Online (Sandbox Code Playgroud)
他们使用的行的含义是什么shift?我认为脚本应该至少与参数一起使用,所以......?
hum*_*ace 202
shift是一个bash内置的,它从参数列表的开头删除参数。鉴于提供给脚本的 3 个参数在$1, $2,中可用$3,那么调用shift  将$2生成新的$1. Ashift 2将移动两个使$1新旧$3。有关更多信息,请参见此处:
Sco*_*ott 47
正如金发姑娘的评论和人类的参考所描述的那样,
shift重新分配位置参数($1,$2等),以便$1采用 的旧值$2,
$2采用 的值$3等。* 
的旧值$1被丢弃。($0未更改。)这样做的一些原因包括:
$10不起作用 - 它被解释为$1与 a 连接0
(因此可能会产生类似Hello0)。在 a 之后shift,第十个参数变为$9。(但是,在大多数现代 shell 中,您可以使用${10}.)for对此要好得多。$1和$2是文本字符串,而$3和所有其他参数是文件名。所以这就是它的表现。假设你的脚本被调用Patryk_script,它被称为
Patryk_script USSR Russia Treaty1 Atlas2 Pravda3
Run Code Online (Sandbox Code Playgroud)
脚本看到
$1 = USSR
$2 = Russia
$3 = Treaty1
$4 = Atlas2
$5 = Pravda3
Run Code Online (Sandbox Code Playgroud)
该语句ostr="$1"将变量设置ostr为USSR。第一条shift语句更改位置参数如下:
$1 = Russia
$2 = Treaty1
$3 = Atlas2
$4 = Pravda3
Run Code Online (Sandbox Code Playgroud)
该语句nstr="$1"将变量设置nstr为Russia。第二个shift语句更改位置参数如下:
$1 = Treaty1
$2 = Atlas2
$3 = Pravda3
Run Code Online (Sandbox Code Playgroud)
然后for循环将文件, , 和中的USSR( $ostr) 变为Russia( $nstr) 。Treaty1Atlas2Pravda3
脚本存在一些问题。
用于 $@ 中的文件;做
如果脚本被调用为
Patryk_script 苏联俄罗斯条约1“世界地图集2”真理报3
它看到
1 美元 = 苏联 2 美元 = 俄罗斯 3 美元 = 条约 1 4 美元 = 世界地图集 2 5 美元 = 真理报 3
但是,因为$@没有被引用,所以空格World Atlas2没有被引用,for循环认为它有四个文件:Treaty1, World, Atlas2, 和Pravda3. 这应该是
对于“$@”中的文件;做
(引用参数中的任何特殊字符)或简单地
为文件做
(相当于更长的版本)。
eval "sed 's/"$ostr"/"$nstr"/g' $file"
不需要将其设为eval,并且将未经检查的用户输入传递给eval可能是危险的。例如,如果脚本被调用为
Patryk_script "'; rm *;'" 俄罗斯条约1 Atlas2 Pravda3
它会执行rm *!如果脚本可以以高于调用它的用户的权限运行,这是一个大问题;例如,如果它可以通过sudoWeb 界面运行或调用。如果您只是在您的目录中将其用作自己,那么这可能并不那么重要。但是可以改成
sed "s/$ostr/$nstr/g" "$file"
这仍然存在一些风险,但风险要小得多。
if [ -f $file ],> $file.tmp和mv $file.tmp $file
应该分别是if [ -f "$file" ],> "$file.tmp"和mv "$file.tmp" "$file",以处理其中可能包含空格(或其他有趣字符)的文件名。(该eval "sed …命令还会对包含空格的文件名进行处理。)
* shift接受一个可选参数:一个正整数,指定要移动的参数数量。默认值为一 ( 1)。例如,shift 4导致$5
成为$1、
$6成为$2、等等。(请注意,Bash 初学者指南中的示例是错误的。)因此您的脚本可以修改为
for file in $@; do
这可能被认为更清楚。
尾注/警告:
Windows 命令提示符(批处理文件)语言也支持一个SHIFT命令,它的作用与shiftUnix shell 中的命令基本相同,但有一个显着区别,我将隐藏它以防止人们被它混淆:
- 类似的命令
 SHIFT 4是一个错误,产生“无效的 SHIFT 命令参数”错误消息。SHIFT /n,其中n是 0 到 8 之间的整数,是有效的——但它不会移动时间。它移动一次,从第n个参数开始。所以 导致(第五个参数)变成变成,依此类推,只留下参数 0 到 3。nSHIFT /4%5%4,%6%5
最简单的解释是这样的。考虑以下命令:
/bin/command.sh SET location Cebu
/bin/command.sh SET location Cebu, Philippines 6014 
Run Code Online (Sandbox Code Playgroud)
如果没有帮助,shift您将无法提取位置的完整值,因为该值可能会变得任意长。当您移动两次时,argsSET和location会被删除,这样:
x="$@"
echo "location = $x"
Run Code Online (Sandbox Code Playgroud)
长时间盯着那个$@东西。这意味着,它可以将完整的位置保存到变量中x,尽管它有空格和逗号。总之,我们调用shift,然后检索变量中剩下的值$@。
更新
我在下面添加了一个非常短的片段,显示了它的概念和用途shift,如果没有它,正确提取字段将非常非常困难。
#!/bin/sh
#
[ $# -eq 0 ] && return 0
a=$1
shift
b=$@
echo $a
[ -n "$b" ] && echo $b
Run Code Online (Sandbox Code Playgroud)
解释:在 后shift,变量b应包含传入的其余内容,无论空格等。这[ -n "$b" ] && echo $b是一种保护,这样我们仅在 b有内容时才打印b 。