두 명령의 출력을 어떻게 다른가요?


165

비슷한 두 디렉토리의 내용을 비교하는 가장 간단한 방법은 다음과 같습니다.

diff `ls old` `ls new`

그러나 왜 이것이 작동하지 않는지 알았습니다. diff내가 원했던 것처럼 두 개의 스트림 대신 명령 줄에 큰 파일 목록을 전달하고 있습니다. 두 출력을 diff로 직접 전달하려면 어떻게합니까?


답변:


246

명령 대체 `…`는 명령 출력을 명령 행으로 대체하므로 diff두 디렉토리의 파일 목록을 인수로 봅니다. 원하는 것은 diff명령 행에서 두 개의 파일 이름을보고이 파일의 내용을 디렉토리 목록으로 만드는 것입니다. 그것이 프로세스 대체 가하는 일입니다.

diff <(ls old) <(ls new)

인수 diff는 다음 /dev/fd/3/dev/fd/4같습니다. 이들은 bash가 작성한 두 개의 파이프에 해당하는 파일 디스크립터입니다. 때 diff이러한 파일을 열고, 그것은 각 파이프의 읽기 측에 연결됩니다. 각 파이프의 쓰기면이 ls명령에 연결됩니다 .


49
echo <(echo) <(echo)이게 너무 재미있을 줄은
Aquarius Power

3
프로세스 대체가 모든 쉘 에서 지원되는 것은 아니지만 파이프 재 지정은 깔끔한 해결 방법 입니다.
Irfan434

1
그냥 LS를 분석하는 것은 권장하지 않습니다 말할 것도 unix.stackexchange.com/questions/128985/why-not-parse-ls을
Katu

@Katu 문제 ls는 파일 이름을 엉망으로 만든다는 것 입니다. 출력 파싱은 취약합니다 ( "이상한"파일 이름에서는 작동하지 않습니다). 두 디렉토리 목록을 비교할 때 출력이 모호하지 않는 한 괜찮습니다. 임의의 파일 이름을 사용하려면와 같은 옵션이 필요합니다 --quoting-style=escape.
Gilles

1
@will <(…)은 파이프를 만듭니다. meld는 파이프와 함께 작동하지 않으므로 사용할 수 없습니다 <(…). zsh을, 당신은 대체 할 수 <(…)=(…)하고 있기 때문에 작동 =(…)임시 파일에 중간 출력을 넣습니다. bash에서는 편리한 구문이 없다고 생각합니다. 임시 파일을 직접 관리해야합니다.
Gilles

3

zsh의 경우를 사용하면 =(command)자동으로 임시 파일이 작성 =(command)되고 파일 자체의 경로로 대체 됩니다. 명령 대체를 사용하면 명령 출력 으로 $(command)대체됩니다 .

따라서 세 가지 옵션이 있습니다.

  1. 명령 대체 : $(...)
  2. 프로세스 대체 : <(...)
  3. zsh가 선호하는 프로세스 대체 : =(...)

zsh flavored process subsitution # 3은 매우 유용하며 diff 도구를 사용하여 두 명령의 출력을 비교하는 데 사용할 수 있습니다 (예 : Beyond Compare).

bcomp  =(ulimit -Sa | sort) =(ulimit -Ha | sort)

Beyond Compare의 경우, 비교를 시작하고 완료 되기를 기다리 므로 bcomp위 대신 (대신 bcompare)에 사용해야합니다 . 을 사용하면 비교가 시작되고 명령 출력을 저장하기 위해 생성 된 임시 파일이 사라져 즉시 종료됩니다.bcompbcompare

자세한 내용은 여기를 참조하십시오 : http://zsh.sourceforge.net/Intro/intro_7.html

또한 이것을 주목하십시오 :

쉘은 임시 파일을 작성하고 명령이 완료되면이를 삭제합니다.

다음은 zsh가 지원하는 두 가지 유형의 프로세스 대체 (즉, # 2 및 # 3)의 차이점입니다.

zsh의 매뉴얼 페이지를 읽으면 <(...)이 = (...)와 비슷한 다른 형식의 프로세스 대체임을 알 수 있습니다. 둘 사이에는 중요한 차이점이 있습니다. <(...)의 경우, 쉘은 파일 대신 명명 된 파이프 (FIFO)를 작성합니다. 파일 시스템을 채우지 않기 때문에 더 좋습니다. 그러나 모든 경우에 작동하지는 않습니다. 실제로 위의 예에서 = (...)을 <(...)으로 바꾸면 fgrep -f <(...)를 제외하고는 모두 작동을 멈췄을 것입니다. 파이프를 편집하거나 메일 폴더로 열 수 없습니다. 그러나 fgrep은 파이프에서 단어 목록을 읽는 데 아무런 문제가 없습니다. foo | 이후 diff <(foo) 막대가 작동하지 않는 이유가 궁금 할 수 있습니다. diff-바 작동; diff가 인수 중 하나가-인 것을 발견하면 임시 파일을 작성하고 표준 입력을 임시 파일에 복사하기 때문입니다.

참조 : https://unix.stackexchange.com/questions/393349/difference-between-subshells-and-process-substitution


2
$(...)프로세스 대체가 아니라 명령 대체입니다. <(...)프로세스 대체입니다. 그것이 인용 된 구절이 전혀 언급되지 않은 이유 $(...)입니다.
muru

2

생선 껍질

피쉬 쉘에서는 psub 로 파이프해야합니다 . Beyond Compare를 사용한 heroku 및 dokku 구성 비교의 예는 다음과 같습니다 .

bcompare (ssh me@myapp.pl dokku config myapp | sort | psub) (heroku config -a myapp | sort | psub)

1
또 다른 그래픽 diff 도구는 meld오픈 소스이며 Ubuntu 및 EPEL 리포지토리에서 사용할 수 있습니다. meldmerge.org
phiphi

0

나는 종종 받아 들인 대답에 설명 된 기술을 사용합니다.

diff <(ls old) <(ls new)

그러나 나는 보통 위의 예보다 훨씬 더 복잡한 명령과 함께 사용한다는 것을 알았습니다. 이러한 경우 diff 명령을 작성하는 것은 성 가실 수 있습니다. 다른 사람들이 유용하게 사용할 수있는 몇 가지 솔루션을 생각해 냈습니다.

나는 diff를 실행하기 전에 관련 명령을 시도하는 시간의 99 %를 발견했습니다. 결과적으로 내가 diff하려는 명령이 내 역사에 있습니다 ... 왜 사용하지 않습니까?

마지막 두 명령을 실행하기 위해 기본 제공 수정 명령 (fc) bash를 사용합니다.

$ echo A
A
$ echo B
B
$ diff --color <( $(fc -ln -1 -1) ) <( $(fc -ln -2 -2 ) )
1c1
< B
---
> A

fc 플래그는 다음과 같습니다.

-n : 숫자가 없습니다. 나열 할 때 명령 번호를 표시하지 않습니다.

-l : Listing : 명령이 표준 출력에 나열됩니다.

-1 -1히스토리에서 시작 및 종료 위치를 참조하며,이 경우 마지막 명령부터 마지막 ​​명령까지만 발생합니다.

마지막으로 $()서브 쉘에서 명령을 실행하기 위해 이것을 랩핑합니다 .

분명히 이것은 입력하기에 약간의 고통이므로 별칭을 만들 수 있습니다.

alias dl='diff --color <( $(fc -ln -1 -1) ) <( $(fc -ln -2 -2 ) )'

또는 함수를 생성 할 수 있습니다 :

dl() {
    if [[ -z "$1" ]]; then
        first="1"
    else
        first="$1"
    fi
    if [[ -z "$2" ]]; then
        last="2"
    else
        last="$2"
    fi
    # shellcheck disable=SC2091
    diff --color <( $(fc -ln "-$first" "-$first") ) <( $(fc -ln "-$last" "-$last") )
}

사용할 히스토리 라인 지정을 지원합니다. 둘 다 사용한 후 별칭이 내가 선호하는 버전이라는 것을 알았습니다.

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