현재 셸 내에서 명령 출력을 실행하는 방법은 무엇입니까?


89

나는 파일에서 내용을 가져와 현재 셸 내에서 실행 하는 source(일명 .) 유틸리티에 대해 잘 알고 있습니다.

이제 일부 텍스트를 셸 명령으로 변환 한 다음 다음과 같이 실행합니다.

$ ls | sed ... | sh

ls임의의 예일 뿐이며 원본 텍스트는 무엇이든 될 수 있습니다. sed또한 텍스트 변형의 예일뿐입니다. 흥미로운 부분은 sh입니다. 나는 내가 가진 것을 파이프 sh하고 그것을 실행합니다.

내 문제는 새 하위 셸을 시작하는 것을 의미합니다. 차라리 현재 셸 내에서 명령을 실행하고 싶습니다. source some-file텍스트 파일에 명령이 있으면 으로 할 수있는 것처럼.

더러워지기 때문에 임시 파일을 만들고 싶지 않습니다.

또는 현재 셸과 똑같은 특성으로 하위 셸을 시작하고 싶습니다.

최신 정보

좋아, 백틱을 사용하는 솔루션은 확실히 작동하지만 출력을 확인하고 변경하는 동안 종종이 작업을 수행해야하므로 결과를 최종적으로 파이프하는 방법이 있다면 훨씬 선호합니다.

슬픈 업데이트

아, /dev/stdin그게 너무 예뻐 보였지만 더 복잡한 경우에는 작동하지 않았습니다.

그래서 나는 이것을 가지고 있습니다.

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/git mv -f $1 $2.doc/i' | source /dev/stdin

모든 .doc파일의 확장자가 소문자인지 확인합니다.

그리고 우연히으로 처리 할 수 xargs있지만 그것은 요점을 벗어납니다.

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/$1 $2.doc/i' | xargs -L1 git mv

그래서 내가 전자를 실행하면 즉시 종료되고 아무 일도 일어나지 않습니다.


먼저 임시 파일로 파이프 한 다음 소싱 할 때 복잡한 명령이 작동합니까? 그렇지 않은 경우 생성 된 출력의 문제점은 무엇입니까? 파일 이름에 공백이 있거나 특정 시퀀스가 ​​제대로 이스케이프되지 않으면 명령 출력이 작동하지 않습니다. 최소한 $ 1 및 $ 2.doc 주위에 따옴표를 추가하고 싶습니다.
Kaleb Pederson

원래 셸에서 이것을 실행해야하는 합당한 이유가 있습니까? -이 예제는 현재 쉘을 조작하지 않으므로 그렇게해도 아무것도 얻지 못합니다. 빠른 해결책은 출력을 파일로 리디렉션하고 해당 파일을 소싱하는 것입니다
nos

@kaleb 출력이 정상적으로 실행됩니다. 이 특별한 경우에, 비록 내가 sh로 파이프하더라도. 파일 이름은 공간에 안전하지만 주목 해 주셔서 감사합니다. 원래 셸의 @nos git 환경 변수. 그리고 이것들은 단지 예일뿐입니다. 문제는 생명입니다.
kch 09.08.15

소스 / dev / stdin은 할당 된 변수가 필요할 때 작동하지 않았습니다. geirha는 Freenode의 강타에 저를 지적 mywiki.wooledge.org/BashFAQ/024 내가 <나를 위해 일한 (명령) 프로세스 대체 소스를 시도 제안
srcerer

답변:


85
$ ls | sed ... | source /dev/stdin

업데이트 : 이 bash는 4.0 작품뿐만 아니라, tcsh를하고, 대시 (변경하는 경우 source.). 분명히 이것은 bash 3.2에서 버그가 있습니다. 로부터 bash는 4.0 릴리스 노트 :

`. '의 원인이되는 버그를 수정했습니다. 장치 또는 명명 된 파이프와 같은 비정규 파일에서 명령을 읽고 실행하는 데 실패합니다.


확인. 그런 다음 bash 4.0으로 이동하십시오.
KCH

아, 그리고 셸을 나열하고 있으므로 zsh에서도 작동합니다.
KCH

1
msys / mingw (/ dev 폴더 (또는 장치)가없는 곳)에서 시도 할 때까지 이것이 독창적이라고 생각했습니다! eval, $ () 및 backticks``의 많은 조합을 시도했습니다. 아무것도 작동하지 못했습니다. , 그래서 마침내 출력을 임시 파일로 리디렉션하고 임시 파일을 소싱 한 다음 제거했습니다. 기본적으로 "sed ...> /tmp/$$.tmp &&. /tmp/$$.tmp && rm / tmp / $$. tmp ". 임시 파일이없는 msys / mingw 솔루션을 가진 사람이
있습니까

10
참고 : 이것은 예상대로 export 문을 처리하지 않는 것 같습니다. 파이프 된 문자열에 export 문이있는 경우 나중에 터미널 환경에서 exportet 변수를 사용할 수없는 반면 eval export도 완벽하게 작동합니다.
Phil

1
MacOS X에는 일반적으로 bash 3.2 (아마도 Yosemite가 4.0입니까?)가 있고 내 사용 사례에서 lastpipe 트릭이 필요하다는 점을 감안할 때 ( 'export'를 추가하기 위해 sed로 각 줄을 사전 처리하여 파일의 모든 변수를 내보내기) 선택했습니다. 로가는 eval "$(sed ...)"방법 -하지만 3.2 버그 헤드 업에 대한 감사합니다!
Alex Dupuy

133

eval명령은 바로이 목적을 위해 존재합니다.

eval "$( ls | sed... )"

bash 매뉴얼 에서 더 :

평가

          eval [arguments]

인수는 단일 명령으로 연결되어 읽혀지고 실행되며 종료 상태는 eval의 종료 상태로 반환됩니다. 인수가 없거나 비어있는 인수 만 있으면 반환 상태는 0입니다.


8
여기서 유일한 문제는 명령을 분리하기 위해;를 삽입해야 할 수도 있다는 것입니다. 이 방법을 직접 사용하여 AIX, Sun, HP 및 Linux에서 작업합니다.
Tanktalus

Tanktalus, 댓글 주셔서 감사합니다. 내 스크립트가 작동하도록 만들었습니다. 내 컴퓨터에서 eval은 줄 바꿈에 대한 명령을 분리하지 않으며 세미콜론으로도 소스를 사용하지 않습니다. 세미콜론을 사용한 평가가 해결책입니다. 나는 당신에게 몇 가지 포인트를 줄 수 있으면 좋겠다.
Milan Babuškov

2
MilanBabuškov @ : 나는 ;-) 당신을 위해 그를 위

3
이것은 훌륭합니다. 그러나 내 유스 케이스의 경우 $ () 주위에 따옴표를 넣어야했습니다. 다음과 같이 eval "$( ssh remote-host 'cat ~/.bash_profile' )"실제로 bash 3.2를 사용하고 있습니다.
Zachary Murray

3
이것은 받아 들인 대답이 아닌 곳에서 작동합니다.
Dan Tenenbaum 2014

37

와, 이것이 오래된 질문이라는 것을 알고 있지만 최근에 똑같은 문제가 있음을 발견했습니다 (그게 내가 여기에 온 방법입니다).

어쨌든- source /dev/stdin대답이 마음에 들지 않지만 더 나은 대답을 찾은 것 같습니다. 사실 믿을 수 없을 정도로 간단합니다.

echo ls -la | xargs xargs

멋지죠? 실제로 이것은 여전히 ​​원하는 것을 수행하지 않습니다. 여러 줄이있는 경우 각 명령을 개별적으로 실행하는 대신 단일 명령으로 연결하기 때문입니다. 그래서 내가 찾은 해결책은 다음과 같습니다.

ls | ... | xargs -L 1 xargs

-L 1옵션은 명령 실행 당 최대 1 줄을 사용함을 의미합니다. 참고 : 줄이 후행 공백으로 끝나면 다음 줄과 연결됩니다! 따라서 각 줄이 공백이 아닌 것으로 끝나는 지 확인하십시오.

마지막으로 할 수 있습니다.

ls | ... | xargs -L 1 xargs -t

실행되는 명령을 확인합니다 (-t는 상세 함).

누군가가 이것을 읽기를 바랍니다!


30

명령의 출력을 소스가 될 수있는 임시 파일로 대체 하는 프로세스 대체를 사용해보십시오 .

source <(echo id)

7
이것은 어떤 종류의 마법입니까?
paulotorrens

이것이 저의 첫 번째 생각이기도했습니다. 평가 솔루션을 더 좋아하지만. @PauloTorrens <(xyz)는 단순히 xyz를 실행하고 <(xyz)를 xyz의 출력을 기록 할 파일의 이름으로 대체합니다. 예를 들어 echo <(echo id), 출력을 제공 /dev/fd/12(12는 예),, cat <(echo id)출력 idsource <(echo id)제공 한 다음 단순히 쓰기와 동일한 출력 을 제공 함으로써 이것이 어떻게 작동하는지 이해하는 것은 정말 쉽습니다.id
netigger

2
@PauloTorrens, 이것을 프로세스 대체 라고 합니다 . 공식적인 설명은 링크 된 문서를 참조하십시오. 그러나 짧은 대답은 "<()"가 이와 같은 경우를 염두에두고 설계된 특수 구문이라는 것입니다.
Mark Stosberg

1
@MarkStosberg, 이것이 특수 구문인지 아니면 단지 (...)리디렉션 된 서브 쉘인지 알고 있습니까?
paulotorrens

2
@PauloTorrens, 프로세스 대체를위한 특수 구문입니다.
마크 Stosberg

6

나는 이것이 질문에 대한 "정답"이라고 믿습니다.

ls | sed ... | while read line; do $line; done

즉, while루프 로 파이프 할 수 있습니다 . read명령에서 명령은 하나 개의 라인을 얻어 stdin변수에 할당하고 $line. $line그런 다음 루프 내에서 실행되는 명령이됩니다. 입력에 더 이상 줄이 없을 때까지 계속됩니다.

이것은 (다른 루프와 같은) 일부 제어 구조에서는 여전히 작동하지 않지만이 경우에는 적합합니다.


5
`ls | sed ...`

나는 좀 ls | sed ... | source -더 예뻐질 것 같은 느낌이 들지만 불행히도 의미를 source이해하지 못합니다 .-stdin


마크 포오의 대답을 보니 이번엔 우리 얼굴이 옳은 것 같지 않나요?
kch

헤, 그래. 나는 그 물건이 존재한다는 것을 결코 기억하지 못합니다.
chaos

1

나는 당신의 해결책이 백틱으로 명령 대체라고 생각합니다 : http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

섹션 3.4.5 참조


3
bash를 사용하는 경우 $( )구문이 선호 될 수 있습니다. '코스 백틱은 더 많은 쉘 에서 작동합니다 ...
dmckee --- ex-moderator kitten

6
$ (command) 및 $ ((1 + 1))은 모든 posix 쉘에서 작동합니다. 많은 사람들이 vim을 구문 오류로 표시함으로써 미루고 있다고 생각하지만, Vim이 거의 사용하지 않는 원래 Bourne 쉘을 강조하기 때문입니다. vim이 올바르게 강조되도록하려면 이것을 .vimrc에 넣으십시오. let g : is_posix = 1
pixelbeat

1

bash 3.2 (macos)에서 mark4o의 솔루션을 사용하려면 다음 예제와 같이 파이프 라인 대신 here 문자열을 사용할 수 있습니다.

. /dev/stdin <<< "$(grep '^alias' ~/.profile)"

또는 백틱 사용 :. / dev / stdin <<<`grep '^ alias'~ / .profile`
William H. Hooper

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