在shell脚本中使用shift的目的是什么?

在shell脚本中使用shift的目的是什么?

我遇到过这个脚本:

#! /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"将变量设置ostrUSSR。第一条shift语句更改位置参数,如下所示:

$1 = Russia
$2 = Treaty1
$3 = Atlas2
$4 = Pravda3

该语句nstr="$1"将变量设置nstrRussia。第二条shift语句更改位置参数,如下所示:

$1 = Treaty1
$2 = Atlas2
$3 = Pravda3

然后循环将文件、和中的( )for更改为( ) 。USSR$ostrRussia$nstrTreaty1Atlas2Pravda3



脚本存在一些问题。

  1. 对于 $@ 中的文件;做

    如果脚本被调用为

    Patryk_script 苏联俄罗斯条约1“世界地图集2”真理报3

    它看到

    1 美元 = 苏联
    2 美元 = 俄罗斯
    $3 = 条约1
    $4 = 世界地图集2
    5 美元=《真理报》3

    但是,因为$@没有被引用,所以 中的空格World Atlas2没有被引用,并且for循环认为它有四个文件:Treaty1WorldAtlas2Pravda3。这应该是

    对于“$@”中的文件;做

    (引用参数中的任何特殊字符)或简单地

    对于文件做

    (相当于更长的版本)。

  2. 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”

    这仍然存在一些风险,但风险要小得多。

  3. if [ -f $file ]> $file.tmpmv $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您将无法提取位置的完整值,因为该值可能会变得任意长。当您移动两次时,argsSETlocation会被删除,这样:

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

相关内容