我遇到过这个脚本:
#! /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
他们使用的线条的含义是什么shift
?我认为该脚本应该至少与参数一起使用,所以......?
答案1
shift
是一个bash
内置函数,它从参数列表的开头删除参数。鉴于提供给脚本的 3 个参数在$1
, $2
,中可用$3
,则调用shift
将生成$2
新的$1
. Ashift 2
将由两个转变为新的$1
旧的$3
。欲了解更多信息,请参见此处:
答案2
正如金发姑娘的评论和人类参考文献所描述的,
shift
重新分配位置参数($1
、$2
等),以便$1
采用 的旧值$2
、 等$2
的旧值。 * 的旧值被丢弃。 (未更改。)这样做的一些原因包括:$3
$1
$0
- 它可以让您更轻松地访问第十个参数(如果有)。
$10
不起作用——它被解释为$1
与 a 连接0
(因此可能会产生类似的结果Hello0
)。 a 之后shift
,第十个参数变为$9
。 (但是,在大多数现代 shell 中,您可以使用${10}
。) - 作为Bash 初学者指南演示了,它可用于循环遍历参数。 IMNSHO,这很笨拙;
for
对此要好得多。 - 正如在您的示例脚本中一样,它可以轻松地以相同的方式处理除少数参数之外的所有参数。例如,在您的脚本中,
$1
和$2
是文本字符串,而$3
和 所有其他参数都是文件名。
其结果如下。假设您的脚本被调用Patryk_script
并且被称为
Patryk_script USSR Russia Treaty1 Atlas2 Pravda3
脚本看到
$1 = USSR
$2 = Russia
$3 = Treaty1
$4 = Atlas2
$5 = Pravda3
该语句ostr="$1"
将变量设置ostr
为USSR
。第一条shift
语句更改位置参数,如下所示:
$1 = Russia
$2 = Treaty1
$3 = Atlas2
$4 = Pravda3
该语句nstr="$1"
将变量设置nstr
为Russia
。第二条shift
语句更改位置参数,如下所示:
$1 = Treaty1
$2 = Atlas2
$3 = Pravda3
然后循环将文件、和中的( )for
更改为( ) 。USSR
$ostr
Russia
$nstr
Treaty1
Atlas2
Pravda3
脚本存在一些问题。
对于 $@ 中的文件;做
如果脚本被调用为
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"
没有必要将其设置为 an
eval
,并且将未经检查的用户输入传递给 aneval
可能会很危险。例如,如果脚本被调用为Patryk_script "'; rm *;'" 俄罗斯条约1 Atlas2 Pravda3
它会执行
rm *
!如果脚本可以以高于调用它的用户的权限运行,那么这是一个大问题;例如,它是否可以sudo
通过 Web 界面运行或调用。如果您只是在目录中自己使用它,那么它可能并不那么重要。但可以改成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 初学者指南是错误的。)所以你的脚本可以修改为
ostr="$1"
nstr="$2"
shift 2
这可能被认为更清楚。
尾注/ 警告:
Windows 命令提示符(批处理文件)语言还支持一个SHIFT
命令,它的功能与 Unix shell 中的命令基本相同shift
,但有一个显着的区别,我将隐藏它以防止人们被它混淆:
- 像这样的命令
SHIFT 4
是一个错误,会产生“SHIFT 命令的参数无效”错误消息。SHIFT /n
, 在哪里n
是 0 到 8 之间的整数,有效 — 但不会移位n
次。转变一次,从...开始这n th 参数。因此,SHIFT /4
导致%5
(第五个参数)成为%4,
%6
,%5
依此类推,仅保留参数 0 到 3。
答案3
最简单的解释是这样的。考虑以下命令:
/bin/command.sh SET location Cebu
/bin/command.sh SET location Cebu, Philippines 6014
如果没有帮助,shift
您将无法提取位置的完整值,因为该值可能会变得任意长。当您移动两次时,argsSET
和location
会被删除,这样:
x="$@"
echo "location = $x"
长时间盯着那个$@
东西。这意味着,它可以将完整的位置保存到变量中,x
尽管它有空格和逗号。总之,我们调用shift
,然后检索变量中剩下的值$@
。
更新
我在下面添加了一个非常短的片段,显示了其概念和用途shift
,如果没有它,正确提取字段将非常非常困难。
#!/bin/sh
#
[ $# -eq 0 ] && return 0
a=$1
shift
b=$@
echo $a
[ -n "$b" ] && echo $b
解释: 后shift
,变量乙应包含传入的其余内容,无论空格等。这[ -n "$b" ] && echo $b
是一种保护,我们只打印乙如果它有内容。
答案4
shift
将命令行参数视为 FIFO 队列,每次调用时都会弹出元素。
array = [a, b, c]
shift equivalent to
array.popleft
[b, c]
$1, $2,$3 can be interpreted as index of the array.
$# is the length of array