하나의 인수에 대해 두 개의 명령을 실행합니다 (스크립트없이)


28

입력을 두 번 입력하지 않고 한 입력에서 두 개의 명령을 수행하려면 어떻게해야합니까?

예를 들어, stat 명령은 파일에 대해 많은 정보를 제공하지만 파일 유형을 나타내지는 않습니다.

stat fileName

파일 명령은 파일이 어떤 유형인지 알려줍니다.

file fileName

이런 식으로 한 줄로 수행 할 수 있습니다.

stat fileName ; file fileName

그러나 fileName을 두 번 입력해야합니다.

입력 또는 입력 변수를 두 번 입력하지 않고 동일한 입력에서 두 명령을 어떻게 실행할 수 있습니까?

Linux에서는 출력을 파이프하는 방법을 알고 있지만 입력을 파이프하는 방법은 무엇입니까?


14
명확하게 말하면, 그것들은 "입력"이 아니라 인수 (일명 매개 ​​변수)입니다. 입력<(파일에서 왼쪽으로 |입력 ) 또는 (스트림에서 오른쪽으로 입력 )을 통해 파이프 될 수 있습니다 . 차이가 있습니다.
goldilocks

6
질문에 대한 대답은 아니지만 문제에 대한 대답 일 수도 있습니다. bash에서 yank-last-arg명령 (기본 단축키 Meta-.또는 Meta-_; Meta종종 왼쪽 Alt키임)은 마지막 매개 변수를 이전 명령 행에서 현재 매개 변수로 복사합니다. 그래서, 실행 한 후 stat fileName다음을 입력 file [Meta-.]하고 입력하지 않아도 fileName다시. 나는 항상 그것을 사용합니다.
Dubu

2
그리고 그 차이가 있다는 것입니다 <하고 >있습니다 리디렉션 하지, 배관 . 파이프 라인은 두 프로세스를 연결하고 리디렉션은 단순히 stdin / stdout을 다시 할당합니다.
bsd

@ bdowning : 예,하지만 그렇지는 않지만 파이프를 리디렉션하고 있습니다. 디스크의 파일에서 읽을 파이프 라인의 시작을 리디렉션하거나 디스크의 파일에 쓸 파이프 라인의 끝을 리디렉션 할 수 있습니다. 여전히 배관. ;-)
James Haigh 2016 년

답변:


21

다른 방법은 다음과 같습니다.

$ stat filename && file "$_"

예:

$ stat /usr/bin/yum && file "$_"
  File: ‘/usr/bin/yum’
  Size: 801         Blocks: 8          IO Block: 4096   regular file
Device: 804h/2052d  Inode: 1189124     Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Context: system_u:object_r:rpm_exec_t:s0
Access: 2014-06-11 22:55:53.783586098 +0700
Modify: 2014-05-22 16:49:35.000000000 +0700
Change: 2014-06-11 19:15:30.047017844 +0700
 Birth: -
/usr/bin/yum: Python script, ASCII text executable

그것은 작동 bash하고 zsh. 그것도 작동 mksh하고 dash있지만 경우에만 상호 작용. AT & T ksh에서는가 file "$_"다른 줄에 있을 때만 작동합니다 stat.


1
!$하나 때문에 작동하지 않습니다 !$(그래서 이전에 입력 된 명령 라인이 아닌 stat 명령의) 마지막 역사 이벤트의 마지막 단어로 확장한다.
Stéphane Chazelas

@ StéphaneChazelas : 아, 그래요, 실수했습니다.
cuonglm

32

아마도 내 너클을 찢어 버릴 것입니다. 여기 배쉬 괄호 확장과 평가의 해키 조합이 있습니다.

eval {stat,file}" fileName;"


2
csh아닌 확장은에서 시작되었습니다 bash. bash에서 복사했습니다 ksh. 이 코드는 모든 csh, tcsh, ksh, zsh, fish 및 bash에서 작동합니다.
Stéphane Chazelas

1
인용문으로 인해 fileName을 "탭 아웃"(자동 완성)하는 기능이 손실되었습니다.
Lonniebiz

미안하지만, 나는 저항 할 수 없었다
tomd

죄송합니다. 여기서 무슨 문제가 eval있습니까? (최고의 의견 참조)
user13107 2016 년

28

을 사용하면 zsh익명 함수를 사용할 수 있습니다.

(){stat $1; file $1} filename

es람다 :

@{stat $1; file $1} filename

당신은 또한 할 수 있습니다 :

{ stat -; file -;} < filename

(가장 stat먼저 file액세스 시간을 업데이트합니다).

나는 할 것이다 :

f=filename; stat "$f"; file "$f"

그러나 그것이 변수에 대한 것입니다.


3
{ stat -; file -;} < filename마법이다. statstdin이 파일에 연결되어 있고 파일 속성을보고하는지 확인해야합니까? 아마도 stdin에서 포인터를 전진시키지 않을 것이므로 filestdin 내용을 스니핑 할 수 있습니다
iruvar

1
@ 1_CR입니다. fd 0 에서 작동하도록 stat -지시 합니다.statfstat()
Stéphane Chazelas

4
{ $COMMAND_A -; $COMMAND_B; } < filename$ COMMAND_A 실제로 어떤 입력을 읽는 경우 변형은 일반적인 경우에 작동하지 않습니다; 예를 들어 { cat -; cat -; } < filename파일 이름의 내용을 한 번만 인쇄합니다.
Max Nanasy 2018 년

2
stat -coreutils-8.0 (2009 년 10 월) 이후로 특수하게 처리 -됩니다. 이전 실험에서 파일을 가져 오지 않은 경우 ( 이 경우 잘못된 결과를 얻을 수있는 경우 가 아니라면) 오류가 발생 합니다.
mr .spuratic

1
@ mr.spuratic. 예, BSD, IRIX 및 zsh 통계는 인식하지 못합니다. zsh 및 IRIX 통계를 stat -f0대신 사용할 수 있습니다 .
Stéphane Chazelas

9

이 모든 대답은 어느 정도 크거나 적은 스크립팅처럼 보입니다. 실제로 스크립팅이 필요하지 않고 쉘에 입력하는 키보드에 앉아 있다고 가정하면 다음을 시도해보십시오.

통계 somefile Enter
파일 Esc_   Enter

bash, zsh 및 ksh에서 적어도 vi 및 emacs 모드 모두에서 Esc 키를 누르면 밑줄을 누르면 이전 줄의 마지막 단어가 현재 줄의 편집 버퍼에 삽입됩니다.

또는 히스토리 대체를 사용할 수 있습니다.

통계 somefile Enter 
파일! $Enter

bash에서 zsh, csh, tcsh 및 대부분의 다른 쉘 !$은 현재 명령 행이 구문 분석 될 때 이전 명령 행의 마지막 단어로 바뀝니다.


zsh / bash에서 사용할 수있는 다른 옵션은 무엇 Esc _입니까? 공식 문서와 연결해 주시겠습니까?
Abhijeet Rastogi 2012 년


1
zsh : linux.die.net/man/1/zshzle 그리고 "insert-last-word"검색
godlygeek

1
@shadyabhi ^ bash와 zsh의 표준 키 바인딩에 대한 링크. bash는 readline을 사용하므로 대부분의 bash는 readline 라이브러리를 사용하는 모든 응용 프로그램에서 작동합니다.
godlygeek

3

내가 합리적으로 단순히 이것을 수행하는 방법은 xargs를 사용하는 것입니다. 파일 / 파이프를 가져 와서 내용을 프로그램 인수로 변환합니다. 스트림을 분할하여 둘 이상의 프로그램으로 보내는 티와 결합 할 수 있습니다.

echo filename | tee >(xargs stat) >(xargs file) | cat

다른 많은 답변과 달리 이것은 Linux의 bash 및 대부분의 다른 쉘에서 작동합니다. 나는 이것이 변수의 좋은 유스 케이스라고 제안하지만 절대적으로 그것을 사용할 수 없다면 파이프와 간단한 유틸리티로만 수행하는 가장 좋은 방법입니다 (특히 멋진 쉘이 없다고 가정).

또한 티 후이 두 프로그램과 동일한 방식으로 추가하여 여러 프로그램에 대해이 작업을 수행 할 수 있습니다.

편집하다

의견에서 제안한 것처럼이 답변에는 몇 가지 결함이 있으며, 첫 번째는 출력 인터레이스의 가능성입니다. 다음과 같이 수정 될 수 있습니다.

echo filename | tee >(xargs stat) >(( wait $!; xargs file )) | cat

그러면 명령이 차례로 실행되고 출력이 인터레이스되지 않습니다.

두 번째 문제는 일부 쉘에서 사용할 수없는 프로세스 대체를 피하는 것입니다. 이는 티 대신 tpipe를 사용하여 얻을 수 있습니다.

echo filename | tpipe 'xargs stat' | ( wait $!; xargs file )

이것은 거의 모든 쉘에 이식 가능해야하며 다른 희망의 ​​문제를 해결하지만 현재 시스템에 사용 가능한 tpipe가 없으므로 메모리 에서이 마지막 것을 작성합니다.


1
프로세스 대체에서만 작동하는 KSH 기능입니다 ksh(안 공개는 변형), bashzsh. 참고 statfile그래서 아마도 자신의 출력이 좌우 될 것이며, 동시에 실행은 (난 당신이 사용하고있는 가정 | cat효과를 한계까지 힘 출력 버퍼링 것을?).
Stéphane Chazelas

@ StéphaneChazelas 당신은 고양이에 대해 정확합니다. 고양이를 사용할 때 나는 그것을 사용할 때 인터레이스 입력 사례를 만들지 못했습니다. 그러나 나는 더 휴대용 솔루션을 순간적으로 생산하기 위해 무언가를 시도 할 것입니다.
Vality

3

이 답변은 다른 몇 가지와 비슷합니다.

통계 파일명   && 파일! # : 1

마지막 답변을 자동으로 반복하는 다른 답변과 달리이전 명령 인수 은 다음을 계산해야합니다.

ls -ld 파일 이름   && 파일! # : 2

다른 답변과 달리 따옴표는 필요하지 않습니다.

stat "useless cat"  &&  file !#:1

또는

echo Sometimes a '$cigar' is just a !#:3.

2

이것은 다른 답변의 몇 가지와 비슷하지만보다 더 이식성이 이것 과에 비해 훨씬 더 간단 이 하나 :

sh -c 'stat "$ 0"; 파일 "$ 0" ' 파일 이름

또는

sh -c 'stat "$ 1"; 파일 "$ 1" 'sh 파일 이름

있는 sh할당됩니다 $0. 입력하는 것이 3 자 더 많지만이 (두 번째) 형식은 $0인라인 스크립트에 부여한 이름 이기 때문에 선호 됩니다. 그것은 그 스크립트의 쉘 오류 메시지에서 예를 들어, 사용되는, 때와 같은 file또는 stat찾을 수 없거나 어떤 이유로 실행할 수 없습니다.


하고 싶다면

stat filename 1  filename 2  filename 3 …; 파일 파일 이름 1  파일 이름 2  파일 이름 3

임의의 수의 파일에 대해서는

sh -c 'stat "$ @"; 파일 "$ @" 'sh 파일 이름 1  파일 이름 2  파일 이름 3

"$@"해당하기 때문에 작동 합니다 "$1" "$2" "$3" ….


이것이 $0인라인 스크립트에 부여하려는 이름입니다. 예를 들어 해당 스크립트의 셸에 의한 오류 메시지에서 사용됩니다. 언제 file또는 stat찾을 수 없거나 어떤 이유로 든 실행할 수없는 경우와 같습니다. 따라서 여기서 의미있는 것을 원합니다. 영감이 없다면을 사용하십시오 sh.
Stéphane Chazelas

1

적어도 당신이하려는 일에 대해 잘못된 질문을하고 있다고 생각합니다. statfile명령은 파일의 이름이 아니라 그것의 내용입니다 매개 변수를하고있다. 나중에 지정한 이름으로 식별 된 파일을 읽는 명령에 있습니다.

파이프에는 두 개의 끝이 있는데, 하나는 입력으로 사용하고 다른 하나는 출력으로 사용하며, 필요한 결과를 얻을 때까지 다른 도구를 사용하여 다른 처리를 수행해야 할 수도 있으므로 의미가 있습니다. 출력을 파이핑하는 방법을 알고 있지만 입력을 파이핑하는 방법을 모르는 것은 원칙적으로 올바르지 않습니다.

귀하의 경우 파일 이름의 형태로 입력을 제공해야하기 때문에 파이프가 전혀 필요하지 않습니다. 그리고 이러한 도구 ( statfile)가 stdin을 읽 더라도 파이프가 두 번째 도구에 도달 할 때 도구 중 하나에 의해 입력이 변경되어서는 안되므로 파이프가 다시 관련이 없습니다.


1

현재 디렉토리의 모든 파일 이름에서 작동합니다.

sh -c "$(printf 'stat -- "$1";file -- "$1";shift%.0b\n' *)" -- *

1
파일 이름에 큰 따옴표, 백 슬래시, 달러, 백틱, }... 문자가없고로 시작하지 않는다고 가정하십시오 -. 일부 printf구현 (zsh, bash, ksh93)에는가 있습니다 %q. 으로 zsh, 그럴 수 있습니다 :printf 'stat %q; file %1$q\n' ./* | zsh
스테판 Chazelas

@StephaneChezales-큰 따옴표의 가능성을 제외하고 모든 것이 수정되었습니다. 나는 sed s///그것을 추측 할 수 있지만, 그 범위에 관한 것입니다-사람들이 파일 이름에 그것을 넣으면 창을 사용해야합니다.
mikeserv 2016 년

@ StéphaneChazelas-nevermind-나는 그들이 다루기가 얼마나 쉬운 지 잊었다.
mikeserv 2016 년

엄밀히 말하면 이것은 질문에 대한 대답이 아닙니다.“ 입력을 두 번 입력하지 않고 같은 입력에서 두 명령을 어떻게 실행할 수 있습니까?”(강조 추가). 답은 현재 디렉토리의 모든 파일에 적용 되며 *두 번 포함 됩니다 . 당신은뿐만 아니라 말할 수 있습니다 stat -- *; file -- *.
Scott

@Scott-입력이 두 번 입력되지 않았습니다- *확장됩니다. 확장*플러스 두 명령 모든 인수에 대한이 * 입력합니다. 만나다? printf commands\ %s\;shift *
mikeserv

1

여기에 '입력'에 대한 몇 가지 다른 참조가 있으므로 먼저 이해하는 몇 가지 시나리오를 제공합니다. 가장 짧은 형태의 질문에 대한 빠른 답변 :

stat testfile < <($1)> outputfile

위의 내용은 테스트 파일에 대한 통계를 수행하고 STDOUT을 취하고 (리디렉션) 다음 특수 함수 (<() 부분)에 포함시킨 다음 최종 결과를 새 파일 (outputfile)로 출력합니다. 파일이 호출 된 다음 bash 내장 (참조 : 새 명령 세트를 시작할 때까지 매번 $ 1)으로 참조됩니다.

귀하의 질문은 훌륭하며이를위한 몇 가지 답변과 방법이 있지만 실제로 귀하가하는 일에 따라 달라집니다.

예를 들어, 루프도 반복 할 수 있습니다. 이것의 일반적인 사용법은 의사 코드 사고 방식에서 다음과 같습니다.

run program < <($output_from_program)> my_own.log

그것을 받아들이고 그 지식을 확장하면 다음과 같은 것들을 만들 수 있습니다.

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

이것은 현재 디렉토리에서 간단한 ls -A를 수행 한 다음 ls -A의 각 결과를 반복하도록 지시합니다 (그리고 여기가 까다로운 곳입니다!) 그 결과 각각에 "thatword"를 grep하고 이전 만 수행하십시오. 실제로 "thatword"가있는 파일을 찾은 경우 printf (빨간색)입니다. 또한 grep 결과를 새로운 텍스트 파일 files_that_matched_thatword에 기록합니다.

출력 예 :

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

index.html

그 모든 것이 단순히 ls -A 결과를 인쇄했습니다. 특별한 것은 없습니다. 이번에 grep 할 무언가를 추가하십시오 :

echo "thatword" >> newfile

이제 다시 실행하십시오.

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

files_that_matched_thatword  index.html  newfile

Found a file: newfile:thatword

아마도 당신이 현재 찾고있는 것보다 더 지루한 대답 일지 모르지만, 이와 같은 편리한 메모를 유지하면 향후 노력에 훨씬 더 도움이 될 것이라고 믿습니다.

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