쉘 스크립트에서 shift를 사용하는 목적은 무엇입니까?


118

이 스크립트를 보았습니다.

#! /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? 스크립트가 적어도 인수와 함께 사용해야한다고 가정합니다 ...?

답변:


141

shiftbash인수 목록의 시작 부분에서 인수를 제거 하는 내장 기능입니다. 스크립트로 제공되는 3 개 인자에서 사용할 수있는 점을 감안 $1, $2, $3, 호출은하는 shift$2새를 $1. A shift 2는 두 가지를 새로운 $1것으로 만들어 옮길 것이다 $3. 자세한 내용은 여기를 참조하십시오.


25
그렇지 않은 한 주 회전 을, 그건 이동 (인수) 배열을 끕니다. Shift / unshift 및 pop / push는 이러한 일반적인 종류의 조작에 대한 일반적인 이름입니다 (예 : bash 's pushdpopd).
goldilocks


@goldilocks는 매우 사실입니다. "회전"대신 "인수 변수에서 인수를 앞으로 이동"과 같은 다른 단어를 사용하는 방법을 더 잘 찾아야했습니다. 어쨌든 나는 텍스트의 예제와 참조에 대한 링크가 그럼에도 불구하고 명확 해지기를 바랍니다.
humanandANDpeace

스크립트는을 언급하지만, bash모든 쉘에 대한 질문이 일반적이므로 Posix 표준이 우선합니다 : pubs.opengroup.org/onlinepubs/9699919799/utilities/…
calandoa

32

골디락스 '주석 인류의 참조는 설명 된 바와 같이, shift(위치 매개 변수를 재 할당하고 $1, $2그 정도로 등) $1의 이전 값을 얻어 $2, $2의 값을 취 $3*   의 값을 구 $1폐기된다. ( $0변경되지 않음)이를 수행하는 몇 가지 이유는 다음과 같습니다.

  • 10 번째 인수 (있는 경우)에 더 쉽게 액세스 할 수 있습니다.  $10작동하지 않습니다 –로 $1연결된 것으로 해석되어 0 (와 같이 생성 될 수 있습니다 Hello0). a shift다음에 열 번째 인수가됩니다 $9. (그러나 대부분의 현대 쉘에서는을 사용할 수 있습니다 ${10}.)
  • 초보자를위한 배쉬 가이드 (Bash Guide for Beginners)가 설명 하듯이 , 인수를 반복하는 데 사용될 수 있습니다. 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.



스크립트에는 몇 가지 문제가 있습니다.

  1. $ @ 파일; 하다

    스크립트가 다음과 같이 호출 된 경우

    Patryk_script 소련 러시아 조약 1 "세계지도 책 2"Pravda3

    그것은 본다

    $ 1 = 소련
    $ 2 = 러시아
    $ 3 = 조약 1
    $ 4 = 세계 아틀라스 2
    5 달러 = Pravda3

    있기 때문에, $@인용되지 않고있는 공간을 World Atlas2인용하고 있지 않습니다 for루프는 네 개의 파일이있다 생각한다 : Treaty1, World, Atlas2,와 Pravda3. 이것은 둘 중 하나 여야합니다

    "$ @"파일; 하다

    (인수에서 특수 문자를 인용) 또는 단순히

    파일을 위해

    (더 긴 버전과 동일).

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

    이것이 될 필요는 없으며 eval, 확인되지 않은 사용자 입력을에 전달하는 eval것은 위험 할 수 있습니다. 예를 들어, 스크립트가 다음과 같이 호출 된 경우

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

    실행됩니다 rm *! 스크립트를 호출 한 사용자의 권한보다 높은 권한으로 스크립트를 실행할 수있는 경우 이는 큰 문제입니다. 예를 들어, sudo웹 인터페이스 를 통해 실행 되거나 호출 될 수있는 경우 . 디렉토리에서 직접 사용하는 경우 그렇게 중요하지 않습니다. 그러나 그것은

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

    여기에는 여전히 몇 가지 위험이 있지만 훨씬 덜 심각합니다.

  3. if [ -f $file ], > $file.tmp그리고 mv $file.tmp $file 해야한다 if [ -f "$file" ], > "$file.tmp"그리고 mv "$file.tmp" "$file"그들에 공백 (또는 다른 이상한 문자를) 할 수도 있습니다 파일 이름을 처리하기 위해, 각각. eval "sed …또한 이 명령은 공백이있는 파일 이름을 엉망으로 만듭니다.


* shift 선택적 인수를 사용합니다. 이동할 매개 변수 수를 지정하는 양의 정수입니다. 기본값은 1 1입니다. 예를 들어, shift 4원인이 $5 되기 위해 $1, $6$2등등합니다. ( 초보자를위한 Bash Guide 의 예제 가 잘못되었습니다.) 따라서 스크립트는 다음과 같이 수정 될 수 있습니다.

ostr="$1"
nstr="$2"
shift 2

더 명확한 것으로 간주 될 수 있습니다.



끝 참고 / 경고 :

Windows 명령 프롬프트 (일괄 파일) 언어 SHIFT는 기본적으로 shiftUnix 쉘 의 명령 과 동일한 기능을 수행 하는 명령을 지원 합니다.

  • SHIFT 4"SHIFT 명령에 잘못된 매개 변수"오류 메시지가 표시되는 오류 와 같은 명령입니다.
  • SHIFT /n, 여기서 n0에서 8 사이의 정수는 유효하지만 시간 은 이동하지 않습니다 . 그것은 한 번 이동 로 시작 N  인수 일. 그래서 원인 (다섯 번째 인수)가되는 이 될 만 3를 통해 인수 0을 떠나, 등등합니다.n SHIFT /4%5%4,  %6%5


2
shift적용 할 수있는 while, until그리고 심지어 for간단한 수있는 것보다 훨씬 더 미묘한 방법으로 배열을 처리하기위한 루프 for루프. 나는 종종 다음 for과 같은 루프 에서 유용하다고 생각합니다 . set -f -- $args; IFS=$split; for arg do set -- $arg; shift "${number_of_fields_to_discard}" && fn_that_wants_split_arg "$arg"; done
mikeserv

3

가장 간단한 설명은 이것입니다. 다음 명령을 고려하십시오.

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

도움 없이는 shift이 값이 임의로 길어질 수 있으므로 위치의 전체 값을 추출 할 수 없습니다. 두 번 교대하면 인수가 다음 SETlocation같이 제거됩니다.

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

$@일을 아주 오래 쳐다 봐 즉, x공백과 쉼표가 있어도 완전한 위치를 변수에 저장할 수 있습니다 . 요약하면, 우리는 호출 shift하고 나중에 변수에서 남은 값을 검색합니다 $@.

업데이트 나는 개념과 유용성을 보여주는 매우 짧은 스 니펫 아래에 shift필드를 올바르게 추출하는 것이 매우 어려울 것입니다.

#!/bin/sh
#

[ $# -eq 0 ] && return 0

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

설명 : 뒤에 shift변수 b 는 공백 등의 문제없이 전달되는 나머지 내용을 포함해야합니다 . 내용이있는 경우 [ -n "$b" ] && echo $b에만 b를 인쇄하도록 보호 합니다.


(1) 이것은 가장 간단한 설명이 아닙니다. 그것은이다 흥미로운 각도. (2) 그러나 많은 인수 (또는 모든 인수)를 연결하려는 한 대신에 사용 습관을들이는 것이 좋습니다 . (3) 나는 당신의 대답을 찬성하려고했지만,“두 번 교대”라고 말하지 않았지만 실제로는 코드로 표시하지 않습니다. (4) 스크립트가 명령 줄에서 여러 단어로 된 값을 가져 오려면 전체 값을 따옴표로 묶는 것이 좋습니다. … (계속)"$*""$@"
Scott

(계속)… 오늘은 신경 쓰지 않아도되지만 접근 방식으로는 여러 개의 공백 ( Philippines   6014), 선행 공백, 후행 공백 또는 탭을 사용할 수 없습니다. (5) BTW, 당신은 또한 bash에서 여기서하고있는 일을 할 수 있습니다 x="${*:3}".
Scott

나는 shift명령이 관련이 없기 때문에 '*** 여러 공백을 허용하지 않습니다 ***'주석이 나오는 곳을 해독하려고했습니다 . $ @와 $ *의 미묘한 차이점을 언급 할 수도 있습니다. 그러나 사실은 여전히 ​​남아 있습니다. 위의 스 니펫은 아무리 많은 공간이나 후행이든 상관없이 위치를 정확하게 추출 할 수 있습니다. 교대 후 위치는 올바른 위치를 포함합니다.
typelogic

2

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.