단일 문에 대해 IFS 설정

단일 문에 대해 IFS 설정

개별 명령/내장 범위에 대해 사용자 정의 IFS 값을 설정하는 것이 가능하다는 것을 알고 있습니다. 개별 명세서에 대해 사용자 정의 IFS 값을 설정하는 방법이 있습니까? 분명히 그렇지 않습니다. 왜냐하면 다음에 따르면 이 작업을 시도할 때 전역 IFS 값이 영향을 받기 때문입니다.

#check environment IFS value, it is space-tab-newline
printf "%s" "$IFS" | od -bc
0000000 040 011 012
             \t  \n
0000003
#invoke built-in with custom IFS
IFS=$'\n' read -r -d '' -a arr <<< "$str"
#environment IFS value remains unchanged as seen below
printf "%s" "$IFS" | od -bc
0000000 040 011 012
             \t  \n
0000003

#now attempt to set IFS for a single statement
IFS=$'\n' a=($str)
#BUT environment IFS value is overwritten as seen below
printf "%s" "$IFS" | od -bc
0000000 012
         \n
     0000001

답변1

일부 쉘( 포함 bash)에서는:

IFS=: command eval 'p=($PATH)'

( sh/POSIX 에뮬레이션이 아닌 경우 를 사용하여 bash생략할 수 있습니다 command.) 그러나 이는 따옴표가 없는 변수를 사용할 때도 종종 필요하며 set -f대부분의 셸에는 로컬 범위가 없습니다.

zsh를 사용하면 다음을 수행할 수 있습니다.

(){ local IFS=:; p=($=PATH); }

$=PATH이는 기본적으로 수행되지 않는 단어 분할을 강제하는 것입니다(변수 확장에는 와일드카드도 없으므로 sh 시뮬레이션을 제외하고는 필요하지 않습니다 zsh).set -f

그러나 에서는zsh$path묶음$PATH또는 구분 기호로 분할: p=(${(s[:])PATH})또는 p=("${(s[:]@)PATH}")빈 요소를 유지합니다.

(){...}(또는 function {...})이라고 불린다.익명 함수일반적으로 로컬 범위를 설정하는 데 사용됩니다. 함수 로컬 범위를 지원하는 다른 셸의 경우 비슷한 작업을 수행할 수 있습니다.

e() { eval "$@"; }
e 'local IFS=:; p=($PATH)'

POSIX 셸에서 변수 및 옵션의 로컬 범위를 지정하려면 다음에서 제공되는 함수를 사용할 수도 있습니다.https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh. 그런 다음 다음과 같이 사용할 수 있습니다.

. /path/to/locvar.sh
var=3,2,2
call eval 'locvar IFS; locopt -f; IFS=,; set -- $var; a=$1 b=$2 c=$3'

(BTW, $PATH위의 분할은 zshIFS가 필드 구분자가 아니라 필드 구분자인 다른 쉘을 제외하고는 유효하지 않습니다.)

IFS=$'\n' a=($str)

.a=1 b=2

메모 var=value cmd:

존재하다:

var=value cmd arg

쉘은 /path/to/cmd새로운 프로세스에서 실행되고 in cmdargin argv[]var=valuein 을 전달합니다 envp[]. 이는 실제로 변수 할당이 아니라 환경 변수를 전달하는 것 이상입니다.처형된주문하다. Bourne 또는 Korn 쉘에서는 set -k작성할 수도 있습니다 cmd var=value arg.

이제 이것은 비-처형된. Bourne 쉘에서는 alone 과 마찬가지로 in이 결국 설정 var=value some-builtin됩니다 . 이는 예를 들어 (쓸모없는)의 동작이 내장 여부에 따라 변경된다는 것을 의미합니다.varvar=valuevar=value echo fooecho

POSIX 및/또는 kshBourne 동작이 다음 클래스에서만 발생하기 때문에 이를 변경했습니다.특수 내장 기능. eval특수 내장형입니다 read. 아니요. 특별하지 않은 내장 명령의 경우 내장 명령의 실행만 var=value builtin설정되어 var외부 명령을 실행할 때와 유사하게 동작합니다.

command명령은 삭제하는 데 사용할 수 있습니다.특별한그 속성특수 내장 기능. POSIX가 무시하는 것은 내장 함수에 대해 eval이는 쉘이 변수 스택을 구현해야 한다는 것을 의미합니다( 명령을 지정하거나 범위를 지정하지 않더라도). 왜냐하면 다음을 수행할 수 있기 때문 입니다 ..localtypeset

a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a

심지어:

a=1 command eval myfunction

사용되거나 설정되고 호출될 수 있는 함수입니다 myfunction.$acommand eval

(AT&T는 여전히 이를 구현하지 않음) (AT&T는 여전히 구현하지 않음) ksh(사양은 대부분 을 기반으로 함 ) 그러나 이제 이 두 가지를 제외한 대부분의 쉘은 구현하기 때문에 이는 실제로 감독이었습니다 . 다른 쉘은 다르게 동작합니다. 예를 들면 다음과 같습니다.kshzsh

a=0; a=1 command eval a=2; echo "$a"

하지만. 이를 지원하는 셸에서 사용하는 것이 local로컬 범위를 구현하는 보다 안정적인 방법입니다.

답변2

Kernighan과 Pike의 "The Unix 프로그래밍 환경"에서 가져온 표준 저장 및 복원:

#!/bin/sh
old_IFS=$IFS
IFS="something_new"
some_program_or_builtin
IFS=${old_IFS}

답변3

질문의 일부 내용은 다음과 같습니다.

IFS=$'\n' a=($str)

왼쪽에서 오른쪽으로 평가되는 두 개의 개별 전역 변수 할당으로 해석됩니다.

IFS=$'\n'; a=($str)

또는

IFS=$'\n'
a=($str)

이는 전역이 수정되는 이유와 IFS토큰이 새 값을 가진 배열 요소로 분할되는 이유를 설명합니다.$strIFS

IFS다음과 같이 수정 효과를 제한하기 위해 서브쉘을 사용할 수 있습니다 .

str="value 0:value 1"
a=( old values )
( # Following code runs in a subshell
 IFS=":"
 a=($str)
 printf 'Subshell IFS: %q\n' "${IFS}"
 echo "Subshell: a[0]='${a[0]}' a[1]='${a[1]}'"
)
printf 'Parent IFS: %q\n' "${IFS}"
echo "Parent: a[0]='${a[0]}' a[1]='${a[1]}'"

a하지만 곧 수정 사항이 서브셸에만 국한된다는 사실을 알게 될 것입니다 .

Subshell IFS: :
Subshell: a[0]='value 0' a[1]='value 1'
Parent IFS: $' \t\n'
Parent: a[0]='old' a[1]='values'

다음으로 다음 솔루션을 사용하여 IFS를 저장/복원할 수 있습니다.이 이전 답변local IFS@msw를 사용하거나 함수 내부를 사용해 보세요.제안을 따르세요@helpermethod로. 그러나 곧 모든 종류의 문제에 직면하게 될 것입니다. 특히 스크립트 호출의 잘못된 동작에 강력해야 하는 라이브러리 작성자라면 더욱 그렇습니다.

  • 처음에 설정되어 있지 않으면 IFS어떻게 되나요 ?
  • set -u(일명 )을 사용하여 실행하면 set -o nounset어떻게 되나요 ?
  • 읽기 전용으로 설정 하면 IFS어떻게 되나요 declare -r IFS?
  • trap재귀 및/또는 비동기 실행(예: 핸들러) 을 처리하기 위해 저장/복원 메커니즘이 필요한 경우 어떻게 해야 합니까?

IFS를 저장/복원하지 마십시오. 대신 임시 수정을 고수하세요.

  • 변수 수정을 단일 명령, 내장 또는 함수 호출로 제한하려면 IFS="value" command.

    • 여러 변수를 특정 문자로 나누어 읽으려면( :아래 예와 같이) 다음을 사용하십시오.

        IFS=":" read -r var1 var2 <<< "$str"
      
    • 배열을 읽으려면 다음을 사용하십시오(대신 다음을 수행하십시오 array_var=( $str )):

        IFS=":" read -r -a array_var <<< "$str"
      
  • 변수 수정의 효과를 서브셸로 제한합니다.

    • 쉼표로 구분된 배열 요소를 출력하려면 다음을 수행하십시오.

        (IFS=","; echo "${array[*]}")
      
    • 문자열로 캡처하려면 다음을 수행하십시오.

        csv="$(IFS=","; echo "${array[*]}")"
      

답변4

이 명령의 경우:

IFS=$'\n' a=($str)

대안적인 해결 방법이 있습니다. 첫 번째 할당( IFS=$'\n')에 실행할 명령(함수)을 지정하는 것입니다.

$ split(){ a=( $str ); }
$ IFS=$'\n' split

이렇게 하면 분할이 호출되는 환경에 IFS가 배치되지만 현재 환경에는 유지되지 않습니다.

이는 또한 항상 위험한 eval 사용을 방지합니다.

관련 정보