_gem_dec() { shift $# ; . /dev/fd/3
} 3<<-FUNC
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
FUNC
for func in guard rspec rake ; do _gem_dec $func ; done
echo "_guard ; _rspec ; _rake are all functions now."
사전 평가 된 유일한 작업 으로 호출 될 때마다 함수에 . source /dev/fd/3공급되는 위의 방법은 하나의 매개 변수를 수신하여이를 대상 및 대상이되는 기능의 이름으로 사전 평가 하는 것입니다._gem_dec()here-document. _gem_dec'sbundle exec
NOTE: . sourcing shell expansions results in twice-evaluated variables - just like eval. It can be risky.
위의 경우에는 위험이 없다고 생각합니다.
위의 코드 블록이로 복사 한 경우 .bashrc파일뿐만 아니라 쉘 기능을합니다 _guard(), _rspec()및_rake() 로그인에서 선언 할 수 있지만 _gem_dec()기능은 쉘에서 언제든지 실행을 위해 사용할 수 프롬프트 (또는 기타) 등 새로운 템플릿 기능을 수행 할 수 있습니다 다음과 같이 필요할 때마다 선언하십시오.
_gem_dec $new_templated_function_name
그리고 @Andrew에게 감사합니다. for loop.
그러나 어떻게?
3위 의 파일 디스크립터를 사용하여 stdin, stdout, and stderr, or <&0 >&1 >&2습관을 없애십시오. 그러나 여기에서 구현하는 몇 가지 기본 예방 조치의 경우와 마찬가지로 결과 함수가 너무 간단하므로 실제로 필요하지 않습니다. 그래도 좋은 습관입니다. shift $#불필요 한 예방 조치 중 하나는 전화하는 것 입니다.
파일은 다음과 같이 지정 될 때 여전히, <input또는>output 함께 [optional num]<file또는 [optional num]>file재 커널은 통해 액세스 할 수있는 파일 기술자로 그것을 읽어 character device에서 특수 파일 /dev/fd/[0-9]*. 경우] [optional num]지정자를 생략하고 0<file입력 및 가정 1>file출력. 이걸 고려하세요:
l='line %d\n' ; printf "$l" 1 2 3 4 5 6 >/dev/fd/1
> line 1
> line 2
> line 3
> line 4
> line 5
> line 6
( printf "$l" 4 5 6 >/dev/fd/3 ; printf "$l" 1 2 3 ) >/tmp/sample 3>/tmp/sample2
( cat /tmp/sample2 ) </tmp/sample
> line 4
> line 5
> line 6
( cat /dev/fd/0 ) </tmp/sample
> line 1
> line 2
> line 3
( cat /dev/fd/3 ) </tmp/sample 3</tmp/sample2
> line 4
> line 5
> line 6
그리고 a here-document는 코드 블록 내에서 파일을 인라인으로 설명하는 수단 일뿐입니다.
<<'HEREDOC'
[$CODE]
HEREDOC
우리는 또한 할 수 있습니다 :
echo '[$CODE]' >/dev/fd/0
하나의 중요한 차이점이 있습니다. 그렇게하지 않으면 의 다음을 쉘은 쉘을 평가합니다 같은 :"'\quote'"<<"'\LIMITER"'here-document$expansion
echo "[$CODE]" >/dev/fd/0
대한 그래서 _gem_dec(), 가 3<<-FUNC here-document입력에 파일이 있다면이 될 것 같은으로 평가 3<~/some.file 제외하고 그 우리가 떠날 때문에 FUNC제한 이 처음에 대한 평가 지수의 자유, $expansion.이것에 대해 중요한 것은 그것이 입력, 의미라는 것이다 그것은 단지 존재 _gem_dec(),하지만, 그것은 또한 이전에 평가되는 _gem_dec()기능을 실행 우리의 쉘 읽고 그 평가하기 때문에 $expansions이전에 입력으로 나눠을 끕니다.
할 수 있습니다 guard,예를 들어 :
_gem_dec guard
따라서 먼저 쉘은 입력을 처리해야합니다.
3<<-FUNC
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
FUNC
파일 디스크립터 3으로 들어가서 쉘 확장을 평가합니다. 이 시점에서 다음을 실행 한 경우 :
cat /dev/fd/3
또는:
cat <&3
둘 다 동등한 명령이므로 * :
_guard() { [ ! -e 'Gemfile' ] && {
command guard "$@" ; return $?
} || bundle exec guard "$@"
}
... 이전에 함수의 모든 코드가 전혀 실행되지 않습니다. 이 함수의 인 <input, 결국. 더 많은 예를 보려면 여기 에서 다른 질문에 대한 내 대답을 참조 하십시오 .
(* 기술적으로 이것은 완전히 사실이 아니다. 나는 선도적 사용하기 때문에 -dash전과 here-doc limiter, 위의 모든 왼쪽 정렬 될 것이다. 그러나 내가 사용하는 -dash내가 수 있도록 <tab-insert>처음부터 읽기 쉽 나는 제거하지 않을거야, 그래서 <tab-inserts>전을 읽기 위해 당신에게 제공 ...)
이것에 대한 가장 좋은 부분은 인용입니다- '"따옴표가 남아 있고 \따옴표 만 제거되었습니다. 쉘을 두 번 평가 해야하는 경우 따옴표 보다 훨씬 쉽기 때문에 $expansion권장 할 것 입니다.here-documenteval
어쨌든, 이제 위의 코드는 함수가 진행되고 입력을 수락하기를 3<~/heredoc.file기다리는 것처럼 정확하게 입력 된 파일과 같습니다 ._gem_dec()/dev/fd/3
그래서 우리가 _gem_dec()첫 번째로 시작할 때 모든 위치 매개 변수를 던지는 것입니다. 다음 단계는 두 번 평가 된 쉘 확장이며 포함 된 $expansions내용이 현재 $1 $2 $3...매개 변수 로 해석 되기를 원하지 않기 때문 입니다. 그래서 나는 :
shift $#
shiftpositional parameters지정한만큼을 버리고 $1남은 것부터 시작합니다 . 소위 나는 경우 _gem_dec one two three프롬프트에서 _gem_dec's $1 $2 $3위치 매개 변수가 될 것입니다 one two three및 총 현재 위치 수, 또는 $#그때라고하는 경우 3. 것 shift 2,의 값을 one하고two 될 shift에드 거리의 값 $1으로 변경됩니다를 three하고 $#로 확장 할 1. 그래서 shift $#단지 그들을 모두 버린다. 이것을하는 것은 엄밀히 예방적인 것이며, 이런 종류의 일을 잠시 동안 한 후에 개발 한 습관 일뿐입니다. 여기 (subshell)에 명확성을 위해 약간 펼쳐져 있습니다.
( set -- one two three ; echo "$1 $2 $3" ; echo $# )
> one two three
> 3
( set -- one two three ; shift 2 ; echo "$1 $2 $3" ; echo $# )
> three
> 1
( set -- one two three ; shift $# ; echo "$1 $2 $3" ; echo $# )
>
> 0
어쨌든, 다음 단계는 마법이 일어나는 곳입니다. 당신이 경우 . ~/some.sh모든 기능 및 환경 변수를 다음 프롬프트 쉘에서에 선언 ~/some.sh한 후 프롬프트 쉘에서 호출 될 것입니다. 동일은 우리를 제외하고, 여기에 해당하는 우리의 파일 기술자를위한 특수 파일, 또는 - 우리 어디있는 우리는 우리의 기능을 선언 한 - 인라인 (in-line) 파일 경로 지정된되었다. 그리고 그것이 작동하는 방식입니다.. sourcecharacter device. /dev/fd/3here-document
_guard
이제 당신의 _guard기능이 무엇을 하든지해야 합니다.
추가:
위치를 저장하는 좋은 방법 :
f() { . /dev/fd/3
} 3<<-ARGS
args='${args:-"$@"}'
ARGS
편집하다:
내가이 질문에 처음 대답했을 때, 나는 어 커가function() 지속 함수로하는 것 보다 현재 쉘 $ENV다림질 에서 지속될 다른 함수를 선언 할 수 있는 쉘을 선언하는 문제에 더 집중했다 . 그 이후로 나는 원래 제안 된 솔루션 3<<-FUNC이 다음 형식 을 취 했음을 깨달았습니다 .
3<<-FUNC
_${1}() {
if [ -e 'Gemfile' ]; then
bundle exec $1 "\$@"
else
command _${1} "\$@"
}
FUNC
내가 특별히에서 선언적 함수의 이름을 변경하기 때문에 아스 커에 대한 예상대로 가능성이 근무하지 않을까요 $1에 _${1}있는 것처럼 호출하는 경우, _gem_dec guard예를 들어,이 발생할 것 _gem_dec이라는 함수 선언 _guard단지 반대를 guard.
참고 : 이러한 행동은 나에게 습관의 문제이다 - 나는 일반적으로 쉘 기능이 차지해야한다는 전제에서 작동 에만 자신의_namespace온 순서 피할 자신의 침입을namespace쉘의commands적절한.
하지만 이것은 등의 아스 커의 사용에 대해서 분명히하는 보편적 인 습관이 아닌 command부르기 $1.
추가 검사를 통해 다음을 믿을 수 있습니다.
아스 커라는 이름의 쉘 기능을 원하는 guard, rspec, or rake호출시에, 새로 컴파일 것 ruby같은 이름의 함수를 if파일이 Gemfile존재 $PATH 또는
if Gemfile 쉘 기능은 실행한다, 존재하지 않는 ruby같은 이름의 기능을.
나는 또한 읽도록 $1요청을 변경했기 때문에 이전에는 작동하지 않았을 것입니다 command.
command _${1}
ruby쉘 함수가 다음과 같이 컴파일 한 함수를 실행하지 않았을 것입니다 .
bundle exec $1
나는 asker가 같은 이름의 쉘 함수를 통해 실행 파일을 호출하는 것을 선호하기 때문에 간접적으로 지정 하기 위해 전혀 사용 하지 않는 것처럼 보일 수 있음을 알 수 있기를 바랍니다 .commandnamespacecommand$PATH
내 분석이 정확하다면 (아커가 확인하기를 바랍니다) 다음과 같습니다.
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
우수한 통화 것을 제외하고 그 조건을 만족해야 guard프롬프트에서하면됩니다 만 에 실행 파일을 실행하려고 시도 $PATH라는 이름의 guard호출 반면 _guard를 확인합니다 프롬프트에 Gemfile's존재하고 그에 따라 컴파일 또는 실행] guard실행 파일을 $PATH. 이런 식 namespace으로 보호되고, 적어도 내가 인식하는 것처럼, asker의 의도는 여전히 충족됩니다.
사실, 우리의 쉘 함수를 추정 _${1}()하고 실행하는 것은 ${PATH}/${1}있습니다 만 우리의 쉘에 전화를 해석 할 수있는 두 가지 방법 중 하나 $1또는 _${1}그 다음의 사용 command모두에서 기능에서 이제 완전히 중복된다. 그럼에도 불구하고, 나는 같은 실수를 두 번 반복하지 않고 계속 남아 있습니다.
이것이 어커에게 받아 들일 수없고 _, 현재의 형식으로 완전히 제거하는 것을 선호 한다면, _underscore내가 이해하는대로 어 커가 요구 사항을 충족시키기 위해 어 커가 편집 해야하는 모든 것이 있어야합니다.
그 외에 변화에서 또한 사용하는 기능을 편집 &&및 / 또는|| 쉘 단락 조건부가 아닌 원래 if/then구. 이런 식으로 command명령문은 평가 모두에서 경우 Gemfile에 있지 않습니다 $PATH. 이 수정은 이벤트 가 존재하지 않는 명령문이 실행 return $?되도록 보장하기 위해 추가를 요구 하지만 함수는 0 이외의 것을 리턴합니다 .bundleGemfileruby $1
마지막으로,이 솔루션은 이식 가능한 쉘 구조 만 구현합니다. 즉, POSIX 호환성을 주장하는 모든 쉘에서 동일한 결과를 생성해야합니다. 물론 모든 POSIX 호환 시스템이 ruby bundle지시문을 처리해야한다고 주장하는 것은 말이되지 않지만 적어도 호출 쉘이 sh또는dash 인지에 관계없이 호출 해야하는 쉘 명령은 동일하게 작동해야합니다 . 또한 위의 의지 작업이 예상대로 (중간-제정신 적어도 염치 shopts어쨌든) 모두 bash와 zsh.
for loop?의미 하는 바에 따르면 ,for loop일반적으로 선언 된 변수는 사라집니다. 같은 이유로 동일한 기능을 기대합니다.