단일 명령 / 내장 범위에 대해 사용자 지정 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
답변
일부 포탄 (포함 bash
) :
IFS=: command eval 'p=($PATH)'
를 사용 하면 sh / POSIX 에뮬레이션에서 if를 bash
생략 할 수 있습니다 command
. 그러나 인용되지 않은 변수를 사용할 때는 일반적으로을 수행해야 set -f
하며 대부분의 셸에는 지역 범위가 없습니다.
zsh를 사용하면 다음을 수행 할 수 있습니다.
(){ local IFS=:; p=($=PATH); }
$=PATH
은 기본적으로 수행되지 않는 단어 분리를 강제로 수행하는 것입니다 zsh
(변수 확장시 글 로빙도 수행되지 않으므로 set -f
sh 에뮬레이션 이 아닌 한 필요하지 않습니다 ).
(){...}
또는 익명 함수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'
( 다른 쉘 $PATH
에서 zsh
와 달리 IFS는 필드 구분자가 아니라 필드 구분 기호입니다.)
IFS=$'\n' a=($str)
같은 두 개의 과제 a=1 b=2
입니다.
에 대한 설명 var=value cmd
:
에서:
var=value cmd arg
셸은 /path/to/cmd
새 프로세스에서 실행 되어 cmd
and arg
in argv[]
및 var=value
in으로 전달 됩니다 envp[]
. 그것은 실제로 변수 할당이 아니라 실행 된 명령에 환경 변수를 전달하는 것 입니다. Bourne 또는 Korn 쉘에서을 사용하여 set -k
작성할 수도 cmd var=value arg
있습니다.
이제는 실행 되지 않는 내장 함수 나 함수에는 적용되지 않습니다 . Bourne 쉘에서 in var=value some-builtin
은 ( var
는) 나중에 var=value
혼자 와 마찬가지로 설정 됩니다. 예를 들어 var=value echo foo
(유용하지 않은) 의 동작 echo
은 내장 여부에 따라 달라집니다 .
POSIX 및 / 또는 ksh
Bourne 동작이 special builtins 라는 내장 범주에 대해서만 발생한다는 점에서 변경되었습니다 . eval
특별한 내장, read
그렇지 않습니다. 비 특수 내장의 경우, 내장 명령 의 실행에 대해서만 var=value builtin
설정 var
하여 외부 명령이 실행될 때와 유사하게 작동합니다.
이 command
명령을 사용하여 해당 특수 내장 의 특수 속성 을 제거 할 수 있습니다 . POSIX가 간과 한 것은 and 내장의 경우 쉘이 변수 스택을 구현해야한다는 것을 의미합니다 ( 또는 범위 제한 명령을 지정하지 않더라도 ).eval
.
local
typeset
a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a
또는:
a=1 command eval myfunction
와 myfunction
함수 인 사용 또는 설정 $a
잠재적 호출 command eval
.
때문 정말 간과했다 ksh
(사양은 대부분의 기반이되는) 그것을 구현하지 않았다 (그리고 AT & T를 ksh
하고 zsh
그것을 구현하는 두, 대부분의 껍질을 제외하고, 현재 아직도 할), 그러나. 동작은 다음과 같이 쉘마다 다릅니다.
a=0; a=1 command eval a=2; echo "$a"
그러나. local
이를 지원하는 쉘에서 사용 하는 것이 로컬 범위를 구현하는보다 안정적인 방법입니다.
답변
Kernighan과 Pike의 “The Unix Programming Environment”에서 가져온 표준 저장 및 복원 :
#!/bin/sh
old_IFS=$IFS
IFS="something_new"
some_program_or_builtin
IFS=${old_IFS}
답변
스크립트를 함수에 넣고 명령 행 인수를 전달하여 해당 함수를 호출하십시오. IFS가 로컬로 정의되었으므로 IFS가 변경되면 글로벌 IFS에 영향을 미치지 않습니다.
main() {
local IFS='/'
# the rest goes here
}
main "$@"
답변
이 명령의 경우 :
IFS=$'\n' a=($str)
대안적인 해결책이 있습니다 : 첫 번째 할당 ( IFS=$'\n'
)에 실행할 명령 (함수)을주는 것 :
$ split(){ a=( $str ); }
$ IFS=$'\n' split
그러면 IFS가 환경에 호출 분할을 배치하지만 현재 환경에서는 유지되지 않습니다.
이것은 또한 항상 위험한 eval의 사용을 피합니다.
답변
@helpermethod의 제안 된 답변은 확실히 흥미로운 접근법입니다. 그러나 BASH 로컬 변수 범위가 호출자에서 호출 된 함수로 확장되기 때문에 약간의 함정입니다. 따라서 main ()에서 IFS를 설정하면 해당 값이 main ()에서 호출 된 함수에 유지됩니다. 예를 들면 다음과 같습니다.
#!/usr/bin/env bash
#
func() {
# local IFS='\'
local args=${@}
echo -n "$FUNCNAME A"
for ((i=0; i<${#args[@]}; i++)); do
printf "[%s]: %s" "${i}" "${args[$i]}"
done
echo
local f_args=( $(echo "${args[0]}") )
echo -n "$FUNCNAME B"
for ((i=0; i<${#f_args[@]}; i++)); do
printf "[%s]: %s" "${i}" "${f_args[$i]} "
done
echo
}
main() {
local IFS='/'
# the rest goes here
local args=${@}
echo -n "$FUNCNAME A"
for ((i=0; i<${#args[@]}; i++)); do
printf "[%s]: %s" "${i}" "${args[$i]}"
done
echo
local m_args=( $(echo "${args[0]}") )
echo -n "$FUNCNAME B"
for ((i=0; i<${#m_args[@]}; i++)); do
printf "[%s]: %s" "${i}" "${m_args[$i]} "
done
echo
func "${m_args[*]}"
}
main "$@"
그리고 출력 …
main A[0]: ick/blick/flick
main B[0]: ick [1]: blick [2]: flick
func A[0]: ick/blick/flick
func B[0]: ick [1]: blick [2]: flick
main ()으로 선언 된 IFS가 func ()에서 여전히 범위 내에 있지 않으면, 배열은 func () B에서 올바르게 구문 분석되지 않았을 것입니다. func ()에서 첫 번째 행의 주석 처리를 제거하면 다음과 같은 결과가 나타납니다.
main A[0]: ick/blick/flick
main B[0]: ick [1]: blick [2]: flick
func A[0]: ick/blick/flick
func B[0]: ick/blick/flick
IFS가 범위를 벗어나면 얻을 수있는 것입니다.
훨씬 더 나은 솔루션 IMHO는 글로벌 / 로컬 수준에서 IFS에 대한 변경 또는 의존을 포기하는 것입니다. 대신, 새로운 쉘을 생성하고 거기에 IFS를 넣습니다. 예를 들어 main ()에서 func ()를 다음과 같이 호출하는 경우 백 슬래시 필드 구분 기호를 사용하여 배열을 문자열로 전달합니다.
func $(IFS='\'; echo "${m_args[*]}")
… IFS 로의 변경은 func ()에 반영되지 않습니다. 배열은 문자열로 전달됩니다.
ick\blick\flick
… func () 내부에서 IFS는 func ()에서 로컬로 변경되지 않는 한 여전히 “/”(main ()에 설정된대로)입니다.
IFS 변경 사항을 격리하는 방법에 대한 자세한 내용은 다음 링크를 참조하십시오.
답변
이 질문의 스 니펫 :
IFS=$'\n' a=($str)
왼쪽에서 오른쪽으로 평가되는 두 개의 개별 전역 변수 할당으로 해석되며 다음과 같습니다.
IFS=$'\n'; a=($str)
또는
IFS=$'\n'
a=($str)
전역 IFS
이 수정 된 이유와 $str
배열 요소로 의 단어 분리가 새로운 값인를 사용하여 수행 된 이유를 설명합니다 IFS
.
서브 쉘을 사용하여 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'
다음으로, @msw 의이 이전 답변 의 솔루션을 사용하여 IFS를 저장 / 복원 하거나 @helpermethod가 제안한local IFS
함수 내부를 사용하려고 합니다. 그러나 곧, 당신은 모든 종류의 문제에 직면하고 있음을 알 수 있습니다. 특히 당신이 스크립트를 잘못 호출하는 것에 대해 강력 해야하는 도서관 저자라면 :
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[*]}")"
-
답변
가장 직접적인 해결책은 $IFS
msw의 답변과 같이 원본의 사본을 만드는 것입니다. 그러나이 솔루션은 빈 문자열과 동일한 세트 IFS
와 IFS
세트를 구분하지 않으므로 많은 응용 프로그램에서 중요합니다. 이 차이점을 포착하는보다 일반적인 해결책은 다음과 같습니다.
# Functions taking care of IFS
set_IFS(){
if [ -z "${IFS+x}" ]; then
IFS_ori="__unset__"
else
IFS_ori="$IFS"
fi
IFS="$1"
}
reset_IFS(){
if [ "${IFS_ori}" == "__unset__" ]; then
unset IFS
else
IFS="${IFS_ori}"
fi
}
# Example of use
set_IFS "something_new"
some_program_or_builtin
reset_IFS