Bash 출력 리디렉션과 관련된 문제


10

마지막 줄을 제외하고 파일의 모든 행을 제거하려고했으나 file.txt가 비어 있지는 않지만 다음 명령이 작동하지 않았습니다.

$cat file.txt |tail -1 > file.txt 

$cat file.txt 

왜 그렇습니까?

19

파일에서 파이프 라인을 통해 다시 동일한 파일로 리디렉션하는 것이 안전하지 않습니다. tail이 첫 번째 단계에서 읽기를 시작하기 전에 파이프 라인의 마지막 단계를 설정할 때 file.txt이 셸에 의해 덮어 쓰여지면 빈 출력으로 끝납니다.

수행하는 대신 다음

tail -1 file.txt >file.txt.new && mv file.txt.new file.txt 

... 음, 사실,하지 않는 생산 코드; 당신은 보안에 민감한 환경에있어 루트로 실행되는 경우 특히, 다음이 더 적합 :

tempfile="$(mktemp file.txt.XXXXXX)" 
chown --reference=file.txt -- "$tempfile" 
chmod --reference=file.txt -- "$tempfile" 
tail -1 file.txt >"$tempfile" && mv -- "$tempfile" file.txt 

또 다른 방법 (암시 적 플랫폼을 만들어 <<<하지 않는 한, 임시 파일을 피하는) 다음은 :

lastline="$(tail -1 file.txt)"; cat >file.txt <<<"$lastline" 

(위의 구현은 bash에만 해당하지만 마지막 줄에 "--version"이있는 경우와 같이 반향이없는 경우에도 작동합니다.)

마지막으로, 하나는 moreutils에서 스폰지를 사용할 수 있습니다

루이스 Baumstark가 말한대로
tail -1 file.txt | sponge file.txt 
  0

tail은 인수로 파일 이름을 허용한다는 점에 유의하십시오. "tail -1 file.txt> file.txt.new && mv file.txt.new file.txt" 23 sep. 082008-09-23 19:35:56

  0

@Marcel Levy - 꽤 좋으며 실행 가능성이 있습니다. 그렇게 효율적으로; 업데이트되었습니다. 23 sep. 082008-09-23 20:47:58

  0

친애하는 @CharlesDuffy, 나는 당신의 "안전한"변형이 사용자가 루트 일 것을 요구한다는 점에 유의하고 싶습니다. 그것이 어떻게 안전한지에 관해서는, 나는 "file.txt"에 대한 액세스 권한과 권한을 적용하기에는 충분하지 않다고 말할 수 있습니다. 그것을 고려해보십시오.txt "사용 권한이 0666 있고 사용 권한이 0700있는"~/.ssh "폴더 안에 배치됩니다.이 경우 내 솔루션은 그 파일을 세계에 노출시키지 않기 때문에 더 안전합니다. 그 때문에 당신의 대답을 downvote해야합니까? ?)) 14 may. 132013-05-14 13:02:27

  0

@CharlesDuffy, Oh ... 누군가가 그 파일에'tail -1 file.txt'의 전체 시간 동안 그것을 노출했기 때문에 한 줄을 추가하면 데이터가 느슨해지는 것을 완전히 잊었습니다. 결과 기존의'file.txt' 파일을 오래된 내용으로 덮어 쓸 수 있습니다. 그 이유 때문에 응답을 downvote해야합니까?;) 14 may. 132013-05-14 13:15:35

  0

@ony 2 차 그룹이 파일을 소유하고있는 경우'chown'을 사용하면 유용 할 수 있습니다. 이 경우에 권한이 필요합니다. 14 may. 132013-05-14 13:18:36

  0

@ race 경쟁 조건은 피할 수 있다면 확실하게 저에게 줄 수 있습니다. 회피가 어떻게 이루어질 것인지 듣고 싶습니다. (특히'sed -i' ,'ed','ex' 그리고 친족도 원자가 아닙니다.) 14 may. 132013-05-14 13:19:43

  0

@Char '~/.ssh/file.txt' 파일에는''.ssh' (일반적으로 0700)와'file.txt (그리고 그 부모 폴더) '. 마지막 항목에 대한 사용 권한 만 복사하므로 잠재적으로 보안 수준이 저하됩니다. 내 솔루션에서는 동일한 파일을 사용하여 (이동/복사/덮어 쓰기없이) 피할 수 있습니다. 그래서이 문제는 피할 수 있습니다 - 이것은 내가 downvote 수 있다는 것을 의미합니까? 물론 내 솔루션에는 다른 문제가 있습니다. 파일의 크기가 내용과 다를 수 있지만 다른 문제입니다. 내 원래의 대답에서는 나는 또한 권한 문제를 피했다. 14 may. 132013-05-14 13:33:32

  0

@ony 이것은 중요한 문제입니다. 임시 파일은 TEMPDIR을 사용하는 대신 최종 위치에 만들어야합니다. 그 변화하기; 포인터 주셔서 감사합니다. 14 may. 132013-05-14 13:38:47

  0

나는 TEMPFILE = $ (임시 파일)이 훨씬 쉽다고 생각한다. 물론 모든 배포판이 이것을 가지고있는 것은 아니지만? 왜 "안전하다"면 .. 궁금합니다. 16 jul. 142014-07-16 15:12:40

  0

@osirisgothra, 실제로 'tempfile'은 배포판마다 다릅니다. OS X에서는 사용할 수 없습니다. 최신 아치 리눅스에서는 사용할 수 없습니다. & c. 그 외에도, 사용자는 주어진 임시 파일이나 디렉토리가 연관된 소프트웨어를 알 수 있으므로 템플릿 매개 변수를 중요한 기능으로 만들 수 있습니다. 16 jul. 142014-07-16 15:57:52


0

동일한 파일 이름으로 다시 쓰는 것은 좋지 않은 것처럼 보입니다. 다음 작업을 수행하는 경우 작동합니다 :

  0

손실 될 것이라는 점을 확실 할 거라고 추측 "에 보인다" , 그리고 리다이 렉션 (truncating 모드에서'anotherfile.txt'의 오픈)은 * 실행 전에 * 발생합니다 (이 경우'tail';'cat'의 실행 전에 일어날 지 여부는 정의되지 않았습니다. 'cat'가 시작하는 데 시간이 필요하기 때문에'cat'이로드가 끝나고 입력을 위해 인수를 열 준비가되기 전에'anotherfile.txt'의 잘라 내기가 이미 일어 났을 가능성이 큽니다. 10 mar. 162016-03-10 19:13:22


1

, 당신이 같은 파일 이름에 쓰고있어 그것을 좋아하지 않는다.

"cat.txt"가 실행되기 전에 셸이 "file.txt"를 열어서 리디렉션을 수행하기 때문에 자르기 때문입니다. 그래서, 당신은 다시 쓰기 전에 파이프 라인의 명령의 실행을 일어날 것이기 때문에 빈 파일을 읽을 고양이의 원인이 파일을 덮어 쓰게됩니다

tail -1 file.txt > file2.txt; mv file2.txt file.txt 

0

tail -1 > file.txt에 있습니다.


3

'cat'이 실행되기 전에 Bash는 이미 'file.txt'를 작성하여 내용을 지우고 있습니다.

일반적으로 동일한 문장에서 읽고있는 파일에 쓰지 마십시오. 위와 같이 다른 파일에 작성하면 moreutils의 스폰지와 같은 유틸리티를 사용하여 해결할 수 있습니다.

$cat file.txt | tail -1 | sponge file.txt
스폰지는 출력 파일을 열기 전에 입력 스트림이 끝날 때까지 대기하기 때문에 작동합니다. 당신이 비난하는 명령 문자열을 제출하면

  0

'cat'은'tail -1 file.txt'에 어떤 값을 주나요? 'file.txt'가 큰 경우에는 ** far ** 효율이 떨어집니다.'tail '이 직접 탐색 가능한 파일 기술자를 가지고 있다면 파일의 마지막 kb로 바로 넘어 가서 읽을 수 있습니다. 마지막 행을 찾으려고 시도하십시오. 마지막 행이 1kb보다 큰 경우 백업하십시오. 그것이 가지고있는 모든 것이'cat'의 파이프 인 경우, 처음부터 파일을 읽어야합니다 - 아무리 큰데도 - 끝까지 도달해야합니다. 21 jul. 162016-07-21 14:42:36

  0

...'tail -1 <file.txt'도 마찬가지로 실제 파일을 찾을 수있는 파일 디스크립터가 될 것이고, stdin (현재'cat file.txt | tail -1' 접근법에 전달 된 명령 행 인자와 동일합니다) . 21 jul. 162016-07-21 14:43:19


2

, 다음 않습니다

  1. 는 I/O 파이프를 작성합니다.
  2. "/ usr/bin/tail -1"을 시작하고, 파이프에서 읽고, file.txt에 기록합니다.
  3. "/ usr/bin/cat file.txt"를 시작하여 파이프에 기록합니다.

'cat'이 (가) 읽기 시작하면 'file.txt'는 이미 '꼬리'에 의해 잘 렸습니다.

이것은 모두 유닉스와 쉘 환경의 디자인의 일부이며, 원래의 본쉘로 다시 돌아갑니다. '기능이 아니라 버그.

  0

(2)와 (3) 사이의 순서가 지정되어 있습니까? 필자의 인상은 그들이 동시에 일어난다는 것인데, 이는 효과적인 경쟁이 일어난다는 것이다.'/ usr/bin/tail '이 exec되기 전에'file.txt'가 쓰기 위해 열리지 만, 잘라내기를 통해 이길 가능성이 높습니다. race ('/ usr/bin/cat'에는 exec가 필요하기 때문에 모든 링커/로더 성능에 영향을 미친다). 21 jul. 162016-07-21 14:46:14


2

tmp = $ (tail -1 file.txt); echo $ tmp> file.txt;

+1

은 임시 파일을 멋지게 피할 수 있지만 모든 표준 문제를 피하려면 인용 부호로 묶어야합니다. 23 sep. 082008-09-23 20:31:58


5

당신은 파일에서 마지막으로 모든 행을 삭제 sed를 사용하지만 수 있습니다

sed -i '$!d' file 
  • -i 장소에서 파일을 대체 할 sed를 알려줍니다; 그렇지 않으면 결과는 STDOUT에 기록됩니다.
  • $은 파일의 마지막 줄과 일치하는 주소입니다.
  • d은 삭제 명령입니다. 이 경우는 에 의해 무효화됩니다!이므로 주소가 일치하는 이 아닌의 모든 행이 삭제됩니다.
  0

이것에 관한 유일한 불행한 점은'sed -i'는 POSIX와 호환되지 않지만 GNU 확장입니다. 'ed'또는 'ex'를 사용하여 내부에서 편집하면 이러한주의 사항을 피할 수 있습니다. 14 may. 132013-05-14 13:16:13


2

이것은 리눅스 쉘에서 잘 작동 : 옵션 dd가 바이트 카운트 (다시 필요하지만, 자리에 다시 필터링 된 내용을 기록한다

replace_with_filter() { 
    local filename="$1"; shift 
    local dd_output byte_count filter_status dd_status 
    dd_output=$("[email protected]" <"$filename" | dd conv=notrunc of="$filename" 2>&1; echo "${PIPESTATUS[@]}") 
    { read; read; read -r byte_count _; read filter_status dd_status; } <<<"$dd_output" 
    ((filter_status > 0)) && return "$filter_status" 
    ((dd_status > 0)) && return "$dd_status" 
    dd bs=1 seek="$byte_count" if=/dev/null of="$filename" 
} 

replace_with_filter file.txt tail -1 

dd의 "notrunc") 실제로 파일을자를 수 있습니다. 새 파일 크기가 이전 파일 크기보다 크거나 같으면 두 번째 dd 호출이 필요하지 않습니다.

파일 복사 방법보다이 방법의 장점은 1) 추가 디스크 공간이 필요하지 않으며 2) 큰 파일에서 더 빠른 성능을 제공하고 3) 순수한 셸 (dd 이외의 파일)입니다.

  0

나는 그것을 좋아한다 - 그것은 혁신적인 아이디어이다. 나는'$ FILTER'의 사용이 http://mywiki.wooledge.org/BashFAQ/050과 충돌하기 때문에이 시점에서 upvoting하지 않을 것입니다. (명령을 올바르게 형성하기 위해 문자열 분할에 의존하는 것은 매우 오류 발생하기 쉽습니다). 13 may. 132013-05-13 14:14:29

  0

무거운 편집 내용에 신경 쓰지 않기를 바랍니다. 이 솔루션은 현재 상당히 견고해야합니다. 14 may. 132013-05-14 13:14:09

  0

나는 그것을 'replace_with_filter'라고 부르지 않을 것이다. 이것은 다음 덩어리마다 원본 덩어리 만 데이터를 생성하는 필터에 대해서만 작동합니다. 그렇지 않으면 필터로 읽은 정보가 출력으로 덮어 쓸 수 있습니다. 마지막'dd' 대신에'truncate -s $ byte_count "$ filename"'을 사용할 수 있습니다. 14 may. 132013-05-14 16:00:28

  0

@ony'truncate'는 POSIX 표준 도구가 아닙니다. 현대적인 coreutils에서 찾아 볼 수 있습니다.하지만 bash를 사용할 수있는 플랫폼이 많이 있지만 그렇지 않습니다. 14 may. 132013-05-14 17:23:21


1

그냥이 경우 사용할 수 있습니다

cat < file.txt | (rm file.txt; tail -1 > file.txt)
"(...)"의 서브 쉘이있는 "cat"연결 직전에 "file.txt"가 열립니다. "rm file.txt"는 디스크에서 참조를 제거하기 전에 subshell에서 "tail"에 대한 쓰기를 위해 열지 만 내용은 stdin을 닫을 때까지 "cat"에 전달 된 열린 디스크립터를 통해 계속 사용할 수 있습니다. 파이프 라인 구성 요소의 시작 사이에 주문의 보장은 없습니다 : 그래서 당신은 더 나은이 명령이 완료됩니다 또는 "file.txt를"의 내용이
에 대한 필요가 없습니다

  0

익명 downvoters는 처벌해야합니다. 이 답변은 작동하며 논리를 포함합니다 (예 : 내용을 읽을 수 있도록 파일을 열어 파일을 삭제). 22 apr. 132013-04-22 09:54:36

  0

나는 실패 사례가 데이터 손실을 가져 오는 해결책은 피할 것입니다 (해당 경고가 명확하게 표시되어 있더라도). 불필요한 서브 쉘이 케이크 위에 장식되어 있습니다. 그리고 "벌을 받았다"? 정말? 13 may. 132013-05-13 14:12:47

  0

@CharlesDuffy, "shoud be punished"는 익명입니다. 이유없이 하향 회선을 사용하는 것은 짜증나게하기 때문입니다. 당신은 그렇게 생각하지 않습니까? 정확성에 관해서는 정확한 대답을 위해서는 높은 보안과 신뢰성이 필요합니까? 때로는 다른 것을 보관하기 위해 무언가를 희생 할 수도 있습니다. 따라서 다양한 관점에서 솔루션을 다르게 처리 할 수 ​​있습니다. 당신이 내 견해에 대한 나의 대답을 투사한다면, 당신은이 대답에 대한 전제 조건이 충족 될 수 없다는 것을 알게 될 것이고, 그것이 잘못된 것이라고 말하는 것은 그릇된 논리입니다. 14 may. 132013-05-14 12:57:46

  0

... 그런데, 명확히하기 위해, 나는 익명의 downvote가 아니었다. 나는 그들의 행동을 방어 할 수 있다고 생각한다. 14 may. 132013-05-14 13:21:23

  0

@CharlesDuffy, ok, downvoter에 대한 가정입니다. 그러나 실제 이유는 다를 수 있습니다. 그리고 그것이 문제입니다. 14 may. 132013-05-14 15:34:09

  0

조금 잘못 인용하자면 : "빌더가 개발자가 소프트웨어를 만드는 방식으로 주택을 지었을 때, 첫 번째 딱다구침이 문명을 파괴 할 것입니다." 사람들이 불필요하게 취약한 접근법을 물론 (특별한 이유가 없으면) 예기치 못하고 문서화되지 않은 고장 모드로 가득 차게하여 문제를 영속시키는 소프트웨어 및 시스템으로 이끌 수 있다고 제안하십시오. 14 may. 132013-05-14 15:51:39

  0

@CharlesDuffy, 파일 내용을 마지막 행으로 대체한다는 사실은 이미 잘못된 경로입니다. 이 파일은 두 개의 개별 파일이어야합니다 (누군가 전체 내용이 필요한 경우). 그렇지 않으면 한 줄에 전체 내용이 아닌 한 줄만 있어야하므로'tail -1'을 수행 할 필요가 없습니다. 그리고 저는이 변형의 다양성이 뇌 맛보기와 더 비슷하다는 사실에서 비롯된 것이라고 확신합니다. 14 may. 132013-05-14 17:00:59

  0

인라인 편집은 사람들이하는 일입니다. 'tail -1'은 특히 좋은 유스 케이스는 아니지만 실제로 시스템 관리 분야에서 경력을 쌓은 사람이라면 실제, 프로덕션 환경 및 스크립트에서 이와 비슷한 방법을 사용하게 될 것입니다. 사소한 주파수. 따라서 좋은 행동의 채택을 장려하고 나쁜 습관의 채택을 방해하는 것은 "두뇌"또는 "아니오"입니다. 14 may. 132013-05-14 17:28:49


1
echo "$(tail -1 file.txt)" > file.txt 
  0

마지막 행에'-n' 만 있으면 어떻게됩니까? 백 슬래시 리터럴이 포함되어 있고 'echo'가 XSI POSIX 확장과 호환된다면 (리터럴이'-e'와 비슷한 것이 없어도 영속 될 수있는 이스케이프가 필요합니다)? echo에 의존하는 것이 아니라'printf '% s \ n' "$ (tail -1 file.txt)"를 사용하는 것이 더 안전합니다 21 jul. 162016-07-21 14:47:42