我遇到过以下脚本:

#! /bin/bash                                                                                                                                                                                           

if (( $# < 3 )); then
  echo "q4312078q old_string new_string file [file...]"
  exit 0
else
  ostr=""; shift
  nstr=""; 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个参数在中可用,则对shift的调用将使成为新的。 >有关更多信息,请参见此处:


http://ss64.com/bash/shift.html
http://www.tldp.org/LDP/Bash -Beginners-Guide / html / sect_09_07.html


评论


+1请注意,它不是在旋转它们,而是将它们移出(参数的)数组。 Shift / unshift和pop / push是这种一般操作的常用名称(例如,参见bash的push和popd)。

–金锁
2014年12月16日22:10

权威参考:gnu.org/software/bash/manual/bashref.html#index-shift

–格伦·杰克曼
2014年12月16日在22:11

@goldilocks非常正确。与其“旋转”,不如我应该更好地找到一种使用另一个单词的方法,例如“将参数在参数变量中前进”。无论如何,我希望本文中的示例和参考文献的链接能够使它们变得清楚。

–人类与和平
2014年12月18日在7:58

尽管脚本中提到了bash,但问题是所有shell都通用的,因此Posix标准适用:pubs.opengroup.org/onlinepubs/9699919799/utilities/…

– calandoa
19年8月14日在11:00

#2 楼

正如goldilocks的评论和人类参考文献所述,
shift重新分配了位置参数(等)
,因此继承了的旧值,
继承了该值的等值*。
的旧值将被丢弃。 (保持不变。)
执行此操作的某些原因包括:


它使您可以更轻松地访问第十个参数(如果有)。
无法正常工作-解释为0Hello0相连
(因此可能会产生类似shift的内容)。
之后,第十个参数变为
(但是,在大多数现代shell中,您可以使用for。)
如Bash初学者指南所示,
它可以用于循环遍历参数。
IMNSHO,这很笨拙; 对此要好得多。
如您的示例脚本中一样,
它使您可以轻松地以相同的方式处理所有参数,除了少数几个。
例如,在您的脚本中,
是文本字符串,
Patryk_script和所有其他参数是文件名。

所以这是它的播放方式。
假设您的脚本名为Patryk_script USSR Russia Treaty1 Atlas2 Pravda3 ,并且名为

  = USSR
 = Russia
 = Treaty1
 = Atlas2
 = Pravda3
 


脚本参见

 ostr="" 


语句ostr将变量USSR设置为shift
第一个 = Russia = Treaty1 = Atlas2 = Pravda3 语句按如下所示更改位置参数:

 nstr="" 


语句nstr将变量Russia设置为shift
第二个 = Treaty1 = Atlas2 = Pravda3 语句按如下方式更改位置参数:

 for 


然后USSR循环将文件$ostrRussia$nstr中的Treaty1Atlas2)更改为Pravda3$@








for file in $@; do


如果脚本被调用为

Patryk_script USSR Russia Treaty1 "World Atlas2" Pravda3


/>会看到

 = USSR
 = Russia
 = Treaty1
 = World Atlas2
 = Pravda3


但是,由于没有引用World Atlas2,所以没有引用for中的空格,
并且Treaty1循环认为它具有四个文件:WorldAtlas2Pravda3
eval
这应该是

for file in "$@"; do


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

for file do


(等效于更长的版本)。


eval "sed 's/"$ostr"/"$nstr"/g' $file"


不需要是eval
和将未经检查的用户输入传递给rm *可能很危险。
例如,如果脚本被调用为

Patryk_script "'; rm *;'" Russia Treaty1 Atlas2 Pravda3


,它将执行sudo
如果可以以比调用脚本的用户更高的特权运行脚本,这将是一个大问题;例如,是否可以通过if [ -f $file ]运行或从Web界面调用。
如果您只是在目录中以自己的方式使用它,可能就没那么重要了。

,但可以将其更改为

sed "s/$ostr/$nstr/g" "$file"


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

> $file.tmpmv $file.tmp $fileif [ -f "$file" ]
应分别为> "$file.tmp"mv "$file.tmp" "$file"eval "sed …
,以处理可能带有空格(或其他有趣字符)的文件名。
shift命令还会处理其中包含空格的文件名。)


* 1带有可选参数:
一个正整数,用于指定多少个参数转移。
默认值为1(shift 4)。
例如,导致
变成ostr="" nstr="" shift 2 ,依此类推。
(请注意,《 Bash初学者指南》中的示例是错误的。)
因此您的脚本可以修改为说

 SHIFT 


,可能会更清楚。



末尾注释/警告:

Windows命令提示符(批处理文件)语言
还支持shift命令,
与Unix shell中的SHIFT 4命令基本相同,
有一个明显的不同,
我将其隐藏起来以防止人们对此感到困惑:




SHIFT /n之类的命令是错误,
产生了“对SHIFT命令无效的参数”错误消息。


n,其中n是0到8之间的整数,
是有效的,但无效移SHIFT /4次。
它从第n个参数开始移动一次。
所以%5导致%4,(第五个参数)变为%6
%5变为q4312079q,依此类推,仅保留参数0到3。



评论


与简单的for循环相比,shift可以应用于while,直到,甚至for循环,以更微妙的方式处理数组。我经常发现它在诸如... set -f-$ args;之类的循环中很有用。 IFS = $ split;对于arg do set-$ arg;移位“ $ {number_of_fields_to_discard}” && fn_that_wants_split_arg“ $ arg”;完成

–mikeserv
2014-12-18在2:37



#3 楼

最简单的解释是这样的。考虑以下命令:

/bin/command.sh SET location Cebu
/bin/command.sh SET location Cebu, Philippines 6014 


如果没有shift的帮助,您将无法提取位置的完整值,因为该值可能会任意长。当您两次移位时,args SETlocation被删除,使得:

x="$@"
echo "location = $x"


凝视$@物体很长。这意味着,尽管有空格和逗号,它也可以将完整位置保存到变量x中。因此,总而言之,我们调用shift,然后再检索变量$@剩余的值。

更新
我在下面添加了一个简短的代码片段,显示了shift的概念和用途,否则,很难正确提取字段。

#!/bin/sh
#

[ $# -eq 0 ] && return 0

a=
shift
b=$@
echo $a
[ -n "$b" ] && echo $b


解释:在shift之后,变量b将包含其余所有传入的内容,无论是空格还是其他。[ -n "$b" ] && echo $b是一种保护,因此我们仅在出现以下情况时才打印b它有内容。

评论


(1)这不是最简单的解释。这是一个有趣的角度。 (2)但是,只要您要串联一堆参数(或所有参数),就可能要养成使用“ $ *”而不是“ $ @”的习惯。 (3)我本来要对您的答案表示赞同,但我没有,因为您说的是“两次移位”,但实际上并没有在代码中显示出来。 (4)如果您希望脚本能够从命令行中获取多个单词的值,最好要求用户将整个值放在引号中。 …(续)

–斯科特
18-2-10的2:15

(续)...您今天可能不在乎,但是您的方法不允许您使用多个空格(Philippines6014),前导空格,尾随空格或制表符。 (5)BTW,您也许还可以使用x =“ $ {*:3}”以bash的方式执行此处的操作。

–斯科特
18-2-10的2:15

我试图解释您的“ ***不允许多个空格***”注释的来源,因为这与shift命令无关。您可能是指$ @与$ *的细微差别。但是事实仍然存在,我上面的代码段可以准确地提取位置,无论尾随或前方有多少空间都没有关系。转移后,位置将包含正确的位置。

–本征场
18-3-11的3:11



#4 楼

shift将命令行参数视为FIFO队列,
每次调用时都将popleft元素。

array = [a, b, c]
shift equivalent to
array.popleft
[b, c]
, , can be interpreted as index of the array.
$# is the length of array