Bash를 사용하여 마지막 명령의 출력을 변수로 자동 캡처합니까?


139

후속 명령에서 마지막으로 실행 된 명령의 결과를 사용할 수 있기를 원합니다. 예를 들어

$ find . -name foo.txt
./home/user/some/directory/foo.txt

이제 편집기에서 파일을 열거 나 삭제하거나 다른 작업을 수행하고 싶다고 가정 해 봅시다.

mv <some-variable-that-contains-the-result> /some/new/location

내가 어떻게 해? bash 변수를 사용하고 있습니까?

최신 정보:

명확히하기 위해 물건을 수동으로 할당하고 싶지 않습니다. 내가 따르는 것은 내장 bash 변수와 같은 것입니다.

ls /tmp
cd $_

$_이전 명령의 마지막 인수를 보유합니다. 비슷한 것을 원하지만 마지막 명령의 출력이 필요합니다.

최종 업데이트 :

세스의 대답은 꽤 잘 작동했습니다. 명심해야 할 몇 가지 :

  • touch /tmp/x솔루션을 처음 시도 할 때 잊지 마십시오
  • 마지막 명령의 종료 코드가 성공한 경우에만 결과가 저장됩니다

편집 내용을 본 후 내 답변을 삭제하려고 생각했습니다. 당신이 찾고있는 것이 내장되어 있는지 궁금합니다.
taskinoor

내장 된 것을 찾을 수 없습니다. 그것을 구현할 수 있는지 궁금합니다. .bahsrc를 통해? 꽤 유용한 기능이라고 생각합니다.
armandino

출력을 파일 또는 파이프로 리디렉션하거나 캡처하는 것만 큼 두려워합니다. 그렇지 않으면 저장되지 않습니다.
bandi

3
쉘과 터미널의 협력 없이는 그렇게 할 수 없으며 일반적으로 협력하지 않습니다. 명령 줄에서 마지막 출력을 어떻게 재사용합니까?를 참조하십시오 . 이전 명령 '출력에서 텍스트 사용 에 대한 유닉스 스택 교환 .
Gilles 'SO- 악마 그만해'

6
명령 출력이 캡처되지 않는 주된 이유 중 하나는 출력이 한 번에 많은 메가 바이트로 임의로 커질 수 있기 때문입니다. 항상 그렇게 크지는 않지만 출력이 크면 문제가 발생합니다.
Jonathan Leffler

답변:


71

이것은 정말 해키 솔루션이지만 대부분 시간이 지나면 작동하는 것 같습니다. 테스트하는 동안 ^C커맨드 라인을 사용할 때 때로는 잘 작동하지 않는 것으로 나타 났지만 조금 더 잘 작동하도록 약간 조정했습니다.

이 해킹은 대화식 모드 해킹 일 뿐이며 다른 사람에게 추천하지 않을 것이라고 확신합니다. 백그라운드 명령은 정상보다 동작이 덜 정의 될 수 있습니다. 다른 답변은 프로그래밍 방식으로 결과를 얻는 더 좋은 방법입니다.


"솔루션"은 다음과 같습니다.

PROMPT_COMMAND='LAST="`cat /tmp/x`"; exec >/dev/tty; exec > >(tee /tmp/x)'

이 bash 환경 변수를 설정하고 원하는대로 명령을 실행하십시오. $LAST일반적으로 찾고있는 출력을 갖습니다.

startide seth> fortune
Courtship to marriage, as a very witty prologue to a very dull play.
                -- William Congreve
startide seth> echo "$LAST"
Courtship to marriage, as a very witty prologue to a very dull play.
                -- William Congreve

1
@armandino : 물론 이것은 표준 출력에서 ​​터미널과 상호 작용할 것으로 예상되는 프로그램이 예상대로 작동하지 않거나 $ LAST (emacs)에 이상한 것을 저장합니다. 그러나 나는 그것이 당신이 얻을만큼 좋은 것이라고 생각합니다. 다른 옵션은 (유형) 스크립트를 사용하여 모든 사본을 파일에 저장 한 다음 PROMPT_COMMAND를 사용하여 마지막 PROMPT_COMMAND 이후의 변경 사항을 확인하는 것입니다. 그러나 여기에는 원하지 않는 것들이 포함됩니다. 그래도 당신이 원하는 것에 더 가까운 것을 찾지 않을 것이라고 확신합니다.
세스 로버트슨

2
더 조금 이동하려면 : PROMPT_COMMAND='last="$(cat /tmp/last)";lasterr="$(cat /tmp/lasterr)"; exec >/dev/tty; exec > >(tee /tmp/last); exec 2>/dev/tty; exec 2> >(tee /tmp/lasterr)'모두 제공하는 $last$lasterr.
ℝ 아 핑크

@cdosborn : 기본적으로 man은 호출기를 통해 출력을 보냅니다. 이전 의견에서 말했듯이 "표준 출력에서 ​​터미널과 상호 작용할 것으로 예상되는 프로그램이 예상대로 작동하지 않습니다." 점점 더 많이 페이저입니다.
세스 로버트슨

No such file or directory
Francisco Corrales Morales

@Raphink 방금 수정 사항을 지적하고 싶었습니다 2> >(tee /tmp/lasterr 1>&2). 표준 출력이 tee표준 오류로 다시 리디렉션되어야 하기 때문에 제안이 끝나야 합니다.
Hugues

90

나는 이것을 자동으로 수행하는 변수를 모른다 . 결과를 복사하여 붙여 넣기하는 것 외에 다른 작업을 수행하려면 예를 들어 방금 수행 한 모든 작업을 다시 실행할 수 있습니다.

vim $(!!)

!!'이전 명령'을 의미하는 히스토리 확장은 어디에 있습니까 ?

적절한 인수 구문 분석을 방해 할 수있는 공백이나 다른 문자가 포함 된 단일 파일 이름이 예상되는 경우 결과를 인용하십시오 ( vim "$(!!)"). 인용 부호가없는 상태로두면 공백이나 다른 쉘 구문 분석 토큰이 포함되어 있지 않으면 여러 파일을 한 번에 열 수 있습니다.


1
복사-붙여 넣기는 일반적으로 명령 출력의 일부만 필요하기 때문에 이러한 경우에 일반적으로 수행하는 작업입니다. 당신이 그것을 언급 한 첫 번째 사람인 것이 놀랍습니다.
Bruno De Fraine

4
키 스트로크를 vim `!!`
줄이면

허용 된 답변과의 차이점을 보려면 date "+%N"다음을 실행 한 다음echo $(!!)
Marinos An

20

배쉬는 못생긴 언어입니다. 예, 출력을 변수에 할당 할 수 있습니다

MY_VAR="$(find -name foo.txt)"
echo "$MY_VAR"

그러나 findBash 변수에 할당 할 때 자동으로 수정되므로 캐리지 리턴 또는 줄 바꿈과 같이 하나의 결과 만 반환하고 그 결과에 "홀수"문자가없는 것이 가장 좋습니다 .

그러나 변수를 사용할 때 변수를 올바르게 인용하는 것이 좋습니다!

그것은 직접 파일을 예와 함께 행동하는 것이 낫다 find'들 -execdir(설명서를 참조).

find -name foo.txt -execdir vim '{}' ';'

또는

find -name foo.txt -execdir rename 's/\.txt$/.xml/' '{}' ';'

5
그들은하는 수 없습니다 당신이 할당 할 때 자동으로 당신이 때이 수정, 수정 에코! 당신 echo "${MY_VAR}"은 이것이 사실임 을 알기 만하면됩니다.
paxdiablo

13

이를 수행하는 방법은 여러 가지가 있습니다. 한 가지 방법은 v=$(command)명령의 출력을에 할당하는 것 v입니다. 예를 들면 다음과 같습니다.

v=$(date)
echo $v

역 따옴표도 사용할 수 있습니다.

v=`date`
echo $v

에서 배쉬 초보자 가이드 ,

이전 스타일의 역 따옴표로 대체 된 형식을 사용하는 경우 백 슬래시는 "$", "`"또는 "\"가 뒤에 나오는 경우를 제외하고 문자 그대로의 의미를 유지합니다. 백 슬래시 앞에 있지 않은 첫 번째 백틱은 명령 대체를 종료합니다. "$ (COMMAND)"양식을 사용할 때 괄호 안의 모든 문자가 명령을 구성합니다. 아무것도 특별하게 취급되지 않습니다.

편집 : 질문에서 편집 한 후에 이것은 OP가 찾고있는 것이 아닌 것 같습니다. 내가 아는 한, $_마지막 명령의 출력 과 같은 특별한 변수는 없습니다 .


11

아주 쉽습니다. 역 따옴표를 사용하십시오.

var=`find . -name foo.txt`

그리고 나중에 언제든지 사용할 수 있습니다

echo $var
mv $var /somewhere

11

Disclamers :

  • 이 답변은 반년 후반입니다. : D
  • 나는 무거운 TMUX 사용자입니다
  • 이것이 작동하려면 tmux에서 쉘을 실행해야합니다

tmux에서 대화식 쉘을 실행할 때 현재 터미널에 표시된 데이터에 쉽게 액세스 할 수 있습니다. 몇 가지 흥미로운 명령을 살펴 보겠습니다.

  • tmux 캡처 창 : 표시된 데이터를 tmux의 내부 버퍼 중 하나에 복사합니다. 현재 보이지 않는 기록을 복사 할 수 있지만 지금은 관심이 없습니다.
  • tmux 목록 버퍼 : 캡처 된 버퍼에 대한 정보를 표시합니다. 최신 번호는 0입니다.
  • tmux show-buffer -b (버퍼 번호) : 터미널에서 주어진 버퍼의 내용을 인쇄합니다
  • tmux paste-buffer -b (buffer num) : 주어진 버퍼의 내용을 입력으로 붙여 넣습니다 .

그래, 이것은 우리에게 지금 많은 가능성을 준다 :) 나에 관해서는, 나는 간단한 별칭을 설정했다 : alias L="tmux capture-pane; tmux showb -b 0 | tail -n 3 | head -n 1"그리고 지금 나는 마지막 줄에 액세스 할 때마다 나는 단순히 사용$(L) 그것을 얻기 위해 한다.

이것은 프로그램이 사용하는 출력 스트림 (stdin 또는 stderr), 인쇄 방법 (ncurses 등) 및 프로그램의 종료 코드와 관계가 없으며 데이터 만 표시하면됩니다.


이 tmux 팁에 감사드립니다. 기본적으로 OP와 동일한 것을 찾고 주석을 찾은 다음 언급 한 tmux 명령과 Vim 모션을 사용하여 이전 명령의 출력 부분을 선택하여 붙여 넣을 수있는 쉘 스크립트를 코딩했습니다 : github.com/ bgribble / lw
Bill Gribble 21

9

쉘을 다음을 포함하는 스크립트로 설정하는 솔루션을 해킹 할 수 있다고 생각합니다.

#!/bin/sh
bash | tee /var/log/bash.out.log

그런 다음 $PROMPT_COMMAND구분 기호를 출력하도록 설정 하면 _해당 로그의 마지막 청크를 얻는 도우미 함수 ( )를 작성할 수 있으므로 다음과 같이 사용할 수 있습니다.

% find lots*of*files
...
% echo "$(_)"
... # same output, but doesn't run the command again

7

bash 프로파일에서 다음 별명을 설정할 수 있습니다.

alias s='it=$($(history | tail -2 | head -1 | cut -d" " -f4-))'

그런 다음 임의의 명령 뒤에 's'를 입력하면 결과를 쉘 변수 'it'에 저장할 수 있습니다.

따라서 사용 예는 다음과 같습니다.

$ which python
/usr/bin/python
$ s
$ file $it
/usr/bin/python: symbolic link to `python2.6'

감사합니다! 이것은 내가 내 구현하는 데 필요한 것입니다 grab기능을하는 클립 보드에 마지막 명령의 사본 n 번째 라인 gist.github.com/davidhq/f37ac87bc77f27c5027e
davidhq

6

방금 bash제안 된 기능 에서이 기능을 증류했습니다 .

grab() {     
  grab=$("$@")
  echo $grab
}

그런 다음, 다음을 수행하십시오.

> grab date
Do 16. Feb 13:05:04 CET 2012
> echo $grab
Do 16. Feb 13:05:04 CET 2012

업데이트 : 대체 제안 익명 사용자 echo에 의해 printf '%s\n'어떤는 같은 프로세스 옵션을하지 않는 장점이있다 -e캡처 한 텍스트를. 따라서 그러한 특성을 기대하거나 경험한다면이 제안을 고려하십시오. 다른 옵션은 cat <<<$grab대신 사용하는 것입니다.


1
"자동"부분이 완전히 없기 때문에이 질문에 대한 답은 아닙니다.
Tilman Vogel

cat <<<$grab옵션 을 설명 할 수 있습니까 ?
leoj

1
인용 man bash: "여기 문자열 : 여기 문서의 변형, 형식은 다음 <<<word과 같습니다 . 단어가 확장되어 표준 입력에서 명령에 제공됩니다." 따라서,의 값 $grab에 공급되는 cat표준 입력에, 그리고 cat그냥 표준 출력에 다시 공급한다.
Tilman Vogel

5

"이후의 명령에서 마지막으로 실행 된 명령의 결과를 사용할 수 있기를 원합니다"라고 말하면 -찾기뿐만 아니라 모든 명령의 결과를 의미한다고 가정합니다.

그렇다면 xargs 는 당신이 찾고있는 것입니다.

find . -name foo.txt -print0 | xargs -0 -I{} mv {} /some/new/location/{}

또는 먼저 출력을보고 싶은 경우 :

find . -name foo.txt -print0

!! | xargs -0 -I{} mv {} /some/new/location/{}

이 명령은 여러 파일을 처리하며 경로 및 / 또는 파일 이름에 공백이 있어도 매력처럼 작동합니다.

명령의 mv {} / some / new / location / {} 부분을 ​​확인하십시오. 이 명령은 이전 명령으로 인쇄 된 각 줄에 대해 빌드 및 실행됩니다. 여기서 이전 명령으로 인쇄 된 행은 {} 대신 대체됩니다 .

xargs 매뉴얼 페이지에서 발췌 :

xargs-표준 입력에서 명령 행 작성 및 실행

자세한 내용은 매뉴얼 페이지를 참조하십시오. man xargs


5

백틱으로 출력을 캡처하십시오.

output=`program arguments`
echo $output
emacs $output

4

나는 일반적으로 다른 사람들이 제안한 것을 할당하지 않고 수행합니다.

$find . -iname '*.cpp' -print
./foo.cpp
./bar.cpp
$vi `!!`
2 files to edit

다음과 같은 경우 더 좋아질 수 있습니다.

$grep -R "some variable" * | grep -v tags
./foo/bar/xxx
./bar/foo/yyy
$vi `!!`

2

마지막 명령을 다시 실행하고 출력을 얻는 것만으로 간단한 bash 변수가 작동합니다.

LAST=`!!`

따라서 다음을 사용하여 출력에서 ​​명령을 실행할 수 있습니다.

yourCommand $LAST

이것은 새로운 프로세스를 생성하고 명령을 다시 실행 한 다음 출력을 제공합니다. 명령 출력을위한 bash 기록 파일 인 것처럼 들립니다. 이것은 bash가 터미널로 보내는 출력을 캡처해야 함을 의미합니다. / dev 또는 / proc가 필요한 것을 볼 수는 있지만 어지럽습니다. 출력 파일로 리디렉션되는 중간에 tee 명령을 사용하여 용어와 bash 사이에 "특수 파이프"를 만들 수도 있습니다.

그러나 둘 다 일종의 해키 솔루션입니다. 가장 좋은 것은 출력 로깅이있는 최신 터미널 인 터미네이터 라고 생각합니다 . 마지막 명령의 결과는 로그 파일을 확인하십시오. 위와 비슷한 bash 변수는 이것을 더 간단하게 만듭니다.


1

명령을 실행하고 결과를 변수에 저장하기로 결정한 후 수행하는 한 가지 방법이 있습니다.

$ find . -name foo.txt
./home/user/some/directory/foo.txt
$ OUTPUT=`!!`
$ echo $OUTPUT
./home/user/some/directory/foo.txt
$ mv $OUTPUT somewhere/else/

또는 변수에 결과가 필요하다는 것을 미리 알고 있다면 백틱을 사용할 수 있습니다.

$ OUTPUT=`find . -name foo.txt`
$ echo $OUTPUT
./home/user/some/directory/foo.txt

1

기존 답변의 대안으로 : while파일 이름에 다음과 같이 공백이 포함될 수있는 경우 사용하십시오 .

find . -name foo.txt | while IFS= read -r var; do
  echo "$var"
done

내가 쓴 것처럼 파일 이름에 공백이 있어야하는 경우에만 차이점이 관련이 있습니다.

주의 : 내장 된 유일한 것은 출력에 관한 것이 아니라 마지막 명령의 상태에 관한 것입니다.


1

!! : 1을 사용할 수 있습니다. 예:

~]$ ls *.~
class1.cpp~ class1.h~ main.cpp~ CMakeList.txt~ 

~]$ rm !!:1
rm class1.cpp~ class1.h~ main.cpp~ CMakeList.txt~ 


~]$ ls file_to_remove1 file_to_remove2
file_to_remove1 file_to_remove2

~]$ rm !!:1
rm file_to_remove1

~]$ rm !!:2
rm file_to_remove2

이 표기는 명령의 번호가 인수를 잡고 : 참조 문서를 "$ {매개 변수 : 오프셋}"여기 : gnu.org/software/bash/manual/html_node/... . 자세한 내용은 여기를 참조하십시오 : howtogeek.com/howto/44997/…
Alexander Bird

1

비슷한 명령이 필요했습니다. 마지막 명령의 출력을 다음 명령에 사용하고 싶었습니다. 많은 것 | (파이프). 예 :

$ which gradle 
/usr/bin/gradle
$ ls -alrt /usr/bin/gradle

같은-

$ which gradle |: ls -altr {}

솔루션 :이 사용자 정의 파이프를 작성했습니다. xargs를 사용하여 정말 간단합니다-

$ alias :='xargs -I{}'

xargs의 짧은 손을 만들어서 기본적으로 아무것도 없으며 매력처럼 작동하며 실제로 편리합니다. 방금 .bash_profile 파일에 별칭을 추가합니다.


1

파일 디스크립터의 마법과 lastpipe 쉘 옵션을 .

스크립트로 수행해야합니다. "lastpipe"옵션은 대화식 모드에서 작동하지 않습니다.

테스트 한 스크립트는 다음과 같습니다.

$ cat shorttest.sh 
#!/bin/bash
shopt -s lastpipe

exit_tests() {
    EXITMSG="$(cat /proc/self/fd/0)"
}

ls /bloop 2>&1 | exit_tests

echo "My output is \"$EXITMSG\""


$ bash shorttest.sh 
My output is "ls: cannot access '/bloop': No such file or directory"

내가 여기서하는 일은 :

  1. 쉘 옵션 설정 shopt -s lastpipe. 파일 디스크립터를 잃을 수 있으므로 이것 없이는 작동하지 않습니다.

  2. 내 stderr도 함께 캡처 확인 2>&1

  3. stdin 파일 디스크립터를 참조 할 수 있도록 출력을 함수로 파이핑

  4. /proc/self/fd/0stdin 인 파일 디스크립터 의 내용을 가져와 변수 설정

스크립트에서 오류를 캡처하는 데 이것을 사용하므로 명령에 문제가 있으면 스크립트 처리를 중지하고 즉시 종료 할 수 있습니다.

shopt -s lastpipe

exit_tests() {
    MYSTUFF="$(cat /proc/self/fd/0)"
    BADLINE=$BASH_LINENO
}

error_msg () {
    echo -e "$0: line $BADLINE\n\t $MYSTUFF"
    exit 1
}

ls /bloop 2>&1 | exit_tests ; [[ "${PIPESTATUS[0]}" == "0" ]] || error_msg

이런 식으로 내가 확인해야 할 2>&1 | exit_tests ; [[ "${PIPESTATUS[0]}" == "0" ]] || error_msg모든 명령 뒤에 추가 할 수 있습니다 .

이제 출력을 즐길 수 있습니다!


0

이것은 bash 솔루션은 아니지만 sed와 함께 pipe를 사용하여 이전 명령 출력의 마지막 행을 얻을 수 있습니다.

먼저 "a"폴더에있는 내용을 볼 수 있습니다

rasjani@helruo-dhcp022206::~$ find a
a
a/foo
a/bar
a/bat
a/baz
rasjani@helruo-dhcp022206::~$ 

그런 다음 ls와 cd를 사용한 예제는 sed & pipe로 다음과 같이 설정됩니다.

rasjani@helruo-dhcp022206::~$ cd `find a |sed '$!d'`
rasjani@helruo-dhcp022206::~/a/baz$ pwd
/home/rasjani/a/baz
rasjani@helruo-dhcp022206::~/a/baz$

따라서 실제 마술은 sed에서 발생합니다. 명령의 결과를 sed 및 sed로 파이프하면 마지막 틱이 백틱과 함께 매개 변수로 사용할 수 있습니다. 또는 xargs에도 결합 할 수 있습니다. (쉘의 "man xargs"는 당신의 친구입니다)


0

쉘에는 마지막 명령의 에코 결과를 저장하는 펄과 같은 특수 기호가 없습니다.

파이프 기호를 awk와 함께 사용하는 방법을 배웁니다.

find . | awk '{ print "FILE:" $0 }'

위의 예에서 다음을 수행 할 수 있습니다.

find . -name "foo.txt" | awk '{ print "mv "$0" ~/bar/" | "sh" }'

0
find . -name foo.txt 1> tmpfile && mv `cat tmpfile` /path/to/some/dir/

더럽지 만 아직 다른 방법입니다.


찾기 . -name foo.txt 1> tmpfile && mv`cat tmpfile` 경로 / to / some / dir && rm tmpfile
Kevin

0

명령의 출력을 특정 파일로 파이프하여 약간 성가 시게하는 것을 기억합니다. 내 솔루션은 내 기능입니다. .bash_profile 파일의 출력을 잡아서 필요할 때 결과를 반환하는 함수입니다.

이 방법의 장점은 전체 명령을 다시 실행할 필요가 없다는 것입니다 ( find중요 할 수있는 장기 실행 명령을 사용하는 경우).

충분히 간단합니다 .bash_profile.

스크립트

# catch stdin, pipe it to stdout and save to a file
catch () { cat - | tee /tmp/catch.out}
# print whatever output was saved to a file
res () { cat /tmp/catch.out }

용법

$ find . -name 'filename' | catch
/path/to/filename

$ res
/path/to/filename

이 시점에서 나는 | catch모든 명령의 끝에 추가하는 경향이 있습니다 . 왜냐하면 비용이 들지 않고 완료하는 데 오랜 시간이 걸리는 명령을 다시 실행해야하기 때문입니다.

또한 텍스트 편집기에서 파일 출력을 열려면 다음을 수행하십시오.

# vim or whatever your favorite text editor is
$ vim <(res)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.