왜“ls | 파일”작업?


32

나는 명령 줄에 대해 연구하고 |(파이프 라인)은 명령의 출력을 다른 명령의 입력으로 리디렉션한다는 것을 알았습니다 . 그렇다면 왜 명령 ls | file이 작동하지 않습니까?

file 입력은 다음과 같은 파일 이름 중 하나입니다. file filename1 filename2

ls출력은 폴더의 디렉토리 및 파일 목록이므로 폴더 ls | file의 모든 파일의 파일 유형을 표시해야한다고 생각했습니다.

그러나 그것을 사용할 때 출력은 다음과 같습니다.

    Usage: file [-bcEhikLlNnprsvz0] [--apple] [--mime-encoding] [--mime-type]
        [-e testname] [-F separator] [-f namefile] [-m magicfiles] file ...
    file -C [-m magicfiles]
    file [--help]

file명령 사용법에 약간의 오류가 있었 으므로


2
plain을 사용하는 경우 ls현재 디렉토리의 모든 파일이 file명령으로 처리됨을 나타냅니다 . ... : 왜 간단하게 수행하지 않습니까? : file *모든 파일, 폴더에 대해 한 줄씩 응답합니다.
Knud Larsen

file *가장 똑똑한 방법 ls입니다. 출력 사용 이 왜 작동하지 않는지 궁금 했습니다. 의심 :)
IanC

6
전제에 결함이 있습니다. "파일 입력은 파일 filename1 filename2와 같은 파일 이름 중 하나입니다."입력되지 않았습니다. @John Kugelman이 아래에 지적한 것처럼 명령 줄 인수입니다.
Monty Harder

3
접선은, 구문 분석은ls 일반적으로 좋은 생각입니다.
kojiro

답변:


71

근본적인 문제는 file파일 이름이 stdin이 아닌 명령 줄 인수로 예상 된다는 것 입니다. 당신이 쓸 때 ls | file의 출력 ls은에 입력으로 전달됩니다 file. 입력이 아닌 인수가 아닙니다.

차이점이 뭐야?

  • 명령 줄 인수는 에서처럼 명령 뒤에 플래그와 파일 이름을 쓸 때 사용됩니다 cmd arg1 arg2 arg3. 쉘 스크립트에서 이러한 인수는 변수로 사용할 수있는 $1, $2, $3, 등으로 C 당신이 그들을 통해 액세스하는 것 char **argvint argc인수에 main().

  • 표준 입력 stdin은 데이터 스트림입니다. 일부 프로그램 에는 명령 행 인수가 제공되지 않을 때 stdin을 읽 cat거나 wc읽습니다. 쉘 스크립트에서는 read한 줄의 입력을 얻는 데 사용할 수 있습니다 . C에서는 다양한 옵션 중에서 scanf()또는 을 사용할 수 getchar()있습니다.

file일반적으로 stdin에서 읽지 않습니다. 적어도 하나의 파일 이름이 인수로 전달 될 것으로 예상합니다. 그래서 ls | file인수를 전달하지 않았기 때문에을 쓸 때 사용법을 인쇄합니다 .

xargs에서와 같이 stdin을 인수로 변환 하는 데 사용할 수 있습니다 ls | xargs file. 여전히 terdon이 언급했듯이 파싱 ls은 나쁜 생각입니다. 이를 수행하는 가장 직접적인 방법은 다음과 같습니다.

file *

2
또는를 file사용하여 입력에서 파일 이름을 강제 로 가져옵니다 ls | file -f -. 아직도 나쁜 생각 ofc.
스펙트럼

2
@Braiam> 그게 요점입니다. 파이프는 stdin ls으로 출력됩니다 file. 사용해보십시오.
스펙트럼

4
@Braiam> 실제로 그것은 낭비적이고 위험합니다. 그러나 작동하지만 OP가 리디렉션 사용을 배우는 경우 더 나은 옵션과 비교하는 것이 좋습니다. 완전성을 위해 나는 또한 file $(ls)다른 방법으로도 작동 한다고 언급 할 수 있습니다 .
스펙트럼

2
나는 모든 답을 읽은 후에는 문제를 더 크게 이해한다고 생각하지만, 실제로 모든 것을 이해하려면 더 읽을 필요가 있다고 생각합니다. 첫째, 분명히 같은 출력을 구문 분석하지 않습니다 배관 및 리디렉션을 사용하여 인수를 하지만 같은, STDIN . 더 잘 이해하기 위해 더 읽어야하지만 표면 검색 인수를 만드는 것은 배열의 프로그램으로 텍스트를 구문 분석하는 것처럼 보이고 STDIN 은 파일이나 출력에 대한 정보를 풀링하는 방법과 같습니다 (모든 프로그램이 설계된 것은 아닙니다) 이 "풀링"으로 작업)
IanC

3
둘째, 파일 이름 목록을 만들기 위해 ls 를 사용하면 파일 이름에는 허용되지만 ls에 잘못된 결과를 초래할 수있는 특수 문자 때문에 나쁜 생각처럼 보입니다 . 파일 이름 사이의 구분자로 줄 바꿈 을 사용하므로 파일 이름에는 줄 바꿈 및 기타 특수 문자 가 포함될 수 있으므로 최종 출력이 정확하지 않을 수 있습니다.
IanC

18

당신이 말했듯이, 입력은 filenamesfile 이어야하기 때문 입니다. 그러나 의 결과는 텍스트입니다. 파일 이름 목록이라는 것은 하드 드라이브의 파일 위치가 아니라 단순히 텍스트라는 사실을 변경하지 않습니다.ls

화면에 출력이 인쇄되면 텍스트가 표시됩니다. 해당 텍스트가 시인 지 또는 파일 이름 목록인지 여부는 컴퓨터와 차이가 없습니다. 아는 것은 텍스트라는 것입니다. 그렇기 때문에 ls텍스트를 입력 으로 사용 하는 프로그램에 출력을 전달할 수 있습니다 ( 실제로해서는 안 됨 ).

$ ls / | grep etc
etc

따라서 파일 이름 을 사용하는 명령에 대한 입력 으로 파일 이름을 텍스트 (예 : ls또는 find)로 나열하는 명령의 출력 을 사용하려면 몇 가지 트릭을 사용해야합니다. 이를위한 일반적인 도구는 다음과 xargs같습니다.

$ ls
file1 file2

$ ls | xargs wc
 9  9 38 file1
 5  5 20 file2
14 14 58 total

앞서 말했듯이, 당신은 정말로의 출력을 파싱하고 싶지 않습니다 ls. 같은 뭔가 find더합니다 ( print0을 인쇄 \0하는 대신 각 파일 이름 뒤에 newilne과 -0의는 xargs이 같은 입력을 처리 할 수 있습니다, 이것은 파일 이름은 줄 바꿈을 포함하여 명령의 작품을 만들 수있는 트릭입니다) :

$ find . -type f -print0 | xargs -0 wc
 9  9 38 ./file1
 5  5 20 ./file2
14 14 58 total

어느 것도 필요하지 않은 자체 방법이 있습니다 xargs.

$ find . -type f -exec wc {} +
 9  9 38 ./file1
 5  5 20 ./file2
14 14 58 total

마지막으로 쉘 루프를 사용할 수도 있습니다. 그러나 대부분의 경우 xargs훨씬 빠르고 효율적입니다. 예를 들면 다음과 같습니다.

$ for file in *; do wc "$file"; done
 9  9 38 file1
 5  5 20 file2

사이드 문제는 즉 file명시 적으로 부여하지 않는 한 실제로 stdin을 읽을 나타나지 않습니다 -자리 : 비교 file foo, echo foo | fileecho foo | file -; 실제로 이것은 아마도 OP의 경우에 사용법 메시지의 이유 일 것입니다 (즉, 실제로 ls는 "단순한 텍스트" 의 출력 때문이 아니라 인수 목록 file이 비어 있기 때문 입니다)
steeldriver

@steeldriver 예. AFAIK는 텍스트가 아닌 파일을 입력해야하는 모든 프로그램에 해당됩니다. 그들은 기본적으로 stdin을 무시합니다. 참고 echo foo | file -실제로 실행되지 않는 file파일에 foo있지만 표준 입력 스트림.
terdon

파일 인수가 주어 졌을 때 cat를 제외하고 stdin을 -제외 하고 는 이상한 오리 (?!) 가 있습니다.
스틸 드라이버

3
이 답변은 stdin과 command line 인수의 차이점을 설명하지 못하므로 허용되는 답변보다 더 중요하지만 여전히 같은 이유로 깊은 오해를 불러 일으 킵니다.
zwol

5
@ terdon이 경우 심각한 오류라고 생각합니다. "파일 (1)하지 표준 입력으로, 명령 줄 인수로 작동하는 파일의 목록을합니다"입니다 기본적인 영업의 명령이 작동하지 않은 이유 이해하고, 구별은 일반적으로 쉘 스크립트 근본적인; 당신은 그들에게 호의를 베풀어 그들에게 호의를 베풀지 않습니다.
zwol

6

'|'을 배웠다 (파이프 라인)은 명령 의 출력 을 다른 명령의 입력으로 리디렉션 하기위한 것입니다.

출력을 "리디렉션"하지는 않지만 프로그램의 출력을 입력으로 사용하고 파일은 입력이 아닌 파일 이름을 인수 로 사용하여 테스트합니다. 리디렉션은 이러한 파일 이름을 파이핑 이 아닌 인수로 전달하지 않으며 나중에 수행하는 작업입니다.

--files-from테스트 할 모든 파일을 나열하는 파일이있는 경우 옵션 을 사용하여 파일에서 파일 이름을 읽으십시오 . 그렇지 않으면 파일의 경로를 인수로 전달하십시오.


6

허용 된 답변은 파이프 명령이 왜 간단하게 작동하지 않는지 설명하고 file *명령을 통해 간단하고 간단한 솔루션을 제공합니다.

언젠가는 유용한 다른 대안을 제안하고 싶습니다. 트릭은 백틱 (`)문자를 사용하고 있습니다. 백틱은 여기 에 매우 자세히 설명되어 있습니다 . 즉, 백틱으로 묶인 명령의 출력을 나머지 명령의 문자열로 대체합니다.

따라서 명령 find `ls`의 출력을 가져 와서 ls명령의 인수로 대체합니다 find. 이것은 허용되는 솔루션보다 길고 복잡하지만 다른 상황에서는 변형이 도움이 될 수 있습니다.


나는 리눅스에서 커맨드 라인을 사용하는 것에 대한 책을 읽고있다. (의심 한 것은 실험을 통해 나왔음) 우연히 나는 "명령 대체"에 대해 읽었다. 당신도 사용할 수 있습니다 $ (명령) 또는 command배쉬에서 명령의 출력을 확장하여 다른 명령에 매개 변수로 사용 (내 휴대 전화에서 백 슬래시 코드를 찾을 수 없습니다). 이 경우 ( ls 와 함께 ) 사용하면 일부 파일 이름의 특수 문자로 인해 여전히 문제가 발생할 수 있습니다.
IanC

@IanC 불행히도, bash에 관한 대부분의 책과 튜토리얼은 쓰레기이며 나쁜 습관, 더 이상 사용되지 않는 구문, 미묘한 버그로 오염되어 있습니다. (유일한) 신뢰할 수있는 참조는 bash 개발자, 즉 프리 노드 의 매뉴얼#bash IRC 채널 입니다 (채널 주제에 링크 된 리소스도 확인하십시오).
ignis

1
명령 대체를 사용하는 것이 때때로 도움이 될 수 있지만,이 맥락에서 특히 ls와는 매우 어색합니다.
Joe


5

ls파이프 를 통한 출력은 각 행을 분리하는 0x0a (즉, 줄 바꿈 문자)를 가진 견고한 데이터 블록이며 file여러 문자가 한 번에 하나씩 작동 할 것으로 예상되는 하나의 매개 변수로 가져옵니다.

일반적으로 ls다른 명령에 대한 데이터 소스를 생성하는 데 사용하지 마십시오. 언젠가는 ..로 파이프 rm되어 문제가 생길 수 있습니다!

for i in *; do file "$i" ; done원하는 출력을 예측할 수 있는 루프를 사용하는 것이 좋습니다 . 공백이있는 파일 이름의 경우 따옴표가 있습니다.


8
file *
easy

3
@IanC 나는 실제로 출력을 파싱하는 ls것이 매우 나쁜 아이디어 라고 충분히 강조 할 수 없다 . 와 같은 유해한 것으로 전달할 수있을뿐만 아니라 rm, 비표준 파일 이름을 손상시키기 때문에 더 중요합니다.
terdon

5
첫 번째 단락은 오해의 소지가 있고 말도 안되는 것입니다. 줄 바꿈은 관련이 없습니다. 두 번째 단락은 잘못된 이유로 옳습니다. ls를 파싱하는 것은 좋지 않지만, 어떻게 든 마술로 rm에 "핍니다".
John Kugelman은 Monica

1
않는 rm표준 입력에서 파일 이름을? 나는 그렇게 생각하지 않는다. 또한 일반적 ls으로 Unix 시작 이후 Unix 파이프 라인 사용을위한 데이터 소스의 주요 예 중 하나였습니다. 출력이 파이프 일 때 출력이 터미널 일 때의 일반적인 기본 형식화와 달리 파이프가 출력 될 때 속성이나 장식이없는 단순한 파일 이름 당 한 줄로 기본 설정되는 이유가 여기에 있습니다.
davidbak

2
@DewiMorgan이 웹 사이트는 주로 비 기술적 인 사용자를 대상으로하므로 여기서 나쁜 습관을 퍼뜨 리거나 장려하는 것은 해를 끼치 지 않으며 아무 것도하지 않습니다. 사용자가 발을 직접 쏘지 않고 발에 매우 ​​가깝게 겨냥한 지식 / 의미를 가지고있는 유닉스 SE 또는 다른 기술 커뮤니티에서, 당신의 요점은 (다른 관행에 관계없이) 유지 될 수 있지만, 여기에서는 귀하의 의견이 똑똑해 보이지 않습니다.
ignis

4

파이프를 file사용하여 피드 하려면 -f일반적으로 파일 이름 뒤에 오는 옵션 을 사용 하지만 단일 하이픈 -을 사용하여 stdin에서 읽을 수도 있습니다.

$ ls
cow.pdf  some.txt
$ ls | file -f -
cow.pdf:       PDF document, version 1.4
some.txt:        ASCII text

하이픈을 사용한 속임수 -는 많은 표준 명령 줄 유틸리티와 함께 ​​작동 --하므로 (때로는 있지만 ) 항상 시도해 볼 가치가 있습니다.

이 도구 xarg는 훨씬 강력하며 대부분의 경우 인수 목록이 너무 긴 경우에만 필요합니다 (자세한 내용은 이 게시물 참조).


언제 --입니까? 나는 그것을 본 적이 없다. --일반적으로 "플래그 끝"표시기입니다.
John Kugelman은 Monica

예,하지만 프로그래머가 그런 식으로 사용하는 몇 가지 사례 (ab)에서 발견했습니다. 나는 정확히 어디에 있는지 기억할 수 없다 (내가 있다면 코멘트를 추가 할 것이다). 나는 그것을 발견했을 때 발언 한 저주를 기억하고이 저주는 분명히 NSFW였다 ;-)
deamentiaemundi

2

아래처럼 사용 명령이 작동합니다.

ls | xargs file

그것은 나에게 더 잘 작동합니다


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