파일에서 줄 순서를 바꾸려면 어떻게해야합니까?


641

각 줄의 내용을 유지하면서 텍스트 파일 (또는 stdin)의 줄 순서를 바꾸고 싶습니다.

즉, 다음으로 시작하십시오.

foo
bar
baz

나는 끝내고 싶다

baz
bar
foo

이에 대한 표준 UNIX 명령 줄 유틸리티가 있습니까?


2
줄을 뒤집는 것에 대한 중요 사항 : 먼저 파일에 줄 바꿈이 있는지 확인하십시오 . 그렇지 않으면 입력 파일의 마지막 두 줄이 출력 파일에서 한 줄로 병합됩니다 (적어도 사용 perl -e 'print reverse <>'하지만 다른 방법에도 적용됨).
— jakub.g


또한 unix.stackexchange.com/questions/9356/…의 거의 복제본 (아직 오래되었지만) . 이 경우 unix.stackexchange.com으로의 마이그레이션이 적합 할 것입니다.
— mc0e

답변:


444

BSD 꼬리 :

tail -r myfile.txt

참조 : FreeBSD , NetBSD , OpenBSD 및 OS X 매뉴얼 페이지.


120
'-r'옵션은 POSIX와 호환되지 않습니다. 아래의 sed 및 awk 솔루션은 가장 저렴한 시스템에서도 작동합니다.
— 총

31
우분투 12.04에서 이것을 시도했지만 내 버전의 tail (8.13)에 -r 옵션이 없다는 것을 알았습니다. 대신 'tac'를 사용하십시오 (아래 Mihai의 답변 참조).
— odigity

12
확인 표시가 아래로 tac으로 이동해야합니다. 수세 11. 우분투 12/13, 페도라 20에 실패 -r 꼬리
— rickfoosusa

2
tail -r ~ / 1 ~ tail : 잘못된 옵션-r 자세한 정보는`tail --help '를 시도하십시오. 새로운 옵션처럼 보입니다
— Bohdan

5
특히 OP가 "표준 UNIX"유틸리티를 요청한 이후에는 이것이 BSD 전용이라는 점을 분명히 대답해야합니다. 이것은 GNU 꼬리가 아니기 때문에 사실상 표준이 아닙니다.
— DanC

1399

또한 언급 할 가치가 있습니다 : tac(the, ahem, reverse of cat). coreutils 의 일부 입니다.

한 파일을 다른 파일로 뒤집기

tac a.txt > b.txt

72
-r 옵션없이 테일 버전을 사용하는 사람들에게 특히 언급 할 가치가 있습니다! (대부분의 Linux 사람들에게는 GNU 꼬리가 있으며 -r이 없으므로 GNU 전술이 있습니다).
— oylenshpeegul 2009

11
사람들이 이전에 tac를 언급했지만 tac는 OS X에 설치되지 않은 것 같습니다. Perl로 대체물을 작성하는 것은 어렵지 않지만 실제는 없습니다.
— Chris Lutz

5
Fink에서 OS X 용 GNU 전술을 얻을 수 있습니다. BSD 꼬리가 아닌 일부 작업을 수행하기 때문에 GNU 꼬리도 얻고 싶습니다.
— oylenshpeegul 2009

25
homebrew와 함께 OS X를 사용하는 경우 brew install coreutils( gtac기본적으로 설치)를 사용하여 tac을 설치할 수 있습니다 .
— Robert

3
문제 중 하나는 파일에 새로운 줄이 없으면 처음 두 줄이 한 줄로 결합 될 수 있다는 것입니다. echo -n "abc\ndee" > test; tac test.
— CMCDragonkai

161

있다 잘 알려진 나오지 트릭 :

# reverse order of lines (emulates "tac")
# bug/feature in HHsed v1.5 causes blank lines to be deleted
sed '1!G;h;$!d'               # method 1
sed -n '1!G;h;$p'             # method 2

(설명 : 버퍼를 유지하기 위해 비 초기 라인 앞에 추가, 라인 교체 및 버퍼 유지, 끝에 라인 인쇄)

또는 awk one-liners에서 (빠른 실행으로) :

awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file*

기억이 안 나면

perl -e 'print reverse <>'

GNU 유틸리티를 갖춘 시스템에서 다른 대답은 더 간단하지만 전 세계가 GNU / Linux는 아닙니다 ...


4
동일한 소스에서 : awk '{a [i ++] = $ 0} END {for (j = i-1; j> = 0;) a [j--]}'파일 인쇄 * sed 및 awk 버전 모두에서 작동 비지 박스 라우터. 'tac'과 'tail-r'은 그렇지 않습니다.
— 총

8
이 답변이 허용되는 답변이기를 바랍니다. cos sed는 항상 사용할 수 있지만 tail -rtac 은 사용할 수 없습니다 .
— ryenus

@ryenus : tac메모리에 맞지 않는 임의의 큰 파일을 처리 할 것으로 예상됩니다 (행 길이는 여전히 제한됨). 해당 sed파일에 대한 솔루션이 작동 하는지 확실하지 않습니다 .
— jfs

그래도 문제 : 기다릴 준비 :-)
— Antoine Lizée

1
더 정확하게 말하면 sed 코드는 O (n ^ 2)이며 큰 파일의 경우 속도가 매우 느릴 수 있습니다. 따라서 awk 대안, 선형에 대한 저의 공감대. 나는 펄 옵션을 시도하지 않았고 배관 친화적이지 않았습니다.
— Antoine Lizée

70

명령이 끝나면 다음을 입력하십시오. | tac

tac은 정확히 당신이 요구하는 것을 수행합니다. "각 줄을 표준 출력에 마지막 줄에 기록하십시오."

tac는 cat :-)의 반대입니다.


왜 그래야합니까? tac명령 의 값을 설명하십시오 . 이는 동일한 주제를 검색하게 된 새로운 사용자에게 유용합니다.
— Nic3500

11
이것은 정말로 받아 들여지는 대답이어야합니다. 부끄러운 표가 너무 많습니다.
— joelittlejohn

62

당신은에 될 일 경우 vim사용

:g/^/m0

5
관련 : 줄 순서를 바꾸는 방법? Vim SE에서
— kenorb

4
내가 한 일을 간단히 설명하면 투표 해 드리겠습니다.
— mc0e

2
그래, 나는 그 비트를 얻지 만, vim 명령의 다양한 비트가 무엇을하고 있는지를 나누는 것을 의미했다. 이제 @kenorb 링크 된 답변을 살펴 보았습니다.이 설명을 제공합니다.
— mc0e

5
g는 "전역 적으로 수행"을 의미합니다. ^는 "행의 시작"을 의미합니다. m은 "행을 새로운 행 번호로 이동합니다. 0은 이동할 행입니다. 0은 "현재 행 1 앞에있는 파일의 맨 위"를 의미합니다. "시작이있는 모든 줄을 찾아서 줄 번호 0으로 옮깁니다." 1 행을 찾아서 맨 위로 이동하십시오. 아무것도하지 않습니다. 그런 다음 2 행을 찾아서 1 행 위의 파일 맨 위로 이동하십시오. 이제 3 호선을 찾아 이동 그것은 정상에. 모든 줄에 대해 이것을 반복하십시오. 마지막에는 마지막 줄을 맨 위로 이동하여 완료합니다. 완료되면 모든 줄을 뒤집 었습니다.
— Ronopolis

: g 전역 명령은 단순히 범위를 사용하는 것보다 매우 특정한 방식으로 작동합니다. 예를 들어 ": % m0"명령은 줄 순서를 바꾸지 않지만 ": % normal ddggP"는 ": g / ^ / normal ddggP"와 같이 줄을 바꾸지 않습니다. 좋은 속임수와 설명 ... 아, 그래, "더 자세한 정보는 : help : g 참조"라는 토큰을 잊어 버렸습니다 ...
— Nathan Chappell

51
tac <file_name>

예:

$ cat file1.txt
1
2
3
4
5

$ tac file1.txt
5
4
3
2
1

42
$ (tac 2> /dev/null || tail -r)

tacLinux에서 작동하는을 사용해보십시오 . 그래도 작동하지 않으면 tail -rBSD 및 OSX에서 작동합니다.


4
왜 tac myfile.txt안되나요?
— Sage

8
@sage, tail -r경우에 대비 하여 tac사용할 수 없습니다. tacPOSIX를 준수하지 않습니다. 둘 다입니다 tail -r. 여전히 완벽한 것은 아니지만 이것은 작동하는 가능성을 향상시킵니다.
— slowpoison

나는 실패한 경우 명령을 수동으로 / 대화식으로 변경할 수없는 경우를 봅니다. 나를 위해 충분합니다.
— 세이지

3
tac이 사용 가능한지 확인하려면 적절한 테스트가 필요합니다. tac사용 가능한 경우 에 발생 하지만 RAM이 부족하고 거대한 입력 스트림을 소비하여 절반을 교환합니다. 실패한 다음 tail -r나머지 스트림 처리에 성공하여 잘못된 결과를 제공합니다.
— mc0e

@PetrPeller OSX 용 homebrew에 대해 Robert의 위의 답변 참조. brew install coreutils 사용 gtac대신에 tac그리고 만약 당신의 별칭으로 추가 전술을 선호 gtac예를 들어 당신이 크로스 플랫폼 (리눅스, OSX)를 사용 쉘 스크립트 원하는 경우
— lacostenycoder

24

다음 명령을 시도하십시오 :

grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"

gawk 문 대신 다음과 같은 작업을 수행합니다. sed 's/^[0-9]*://g'
— bng44270

2
grep -n 대신 "nl"을 사용하지 않는 이유는 무엇입니까?
— Good Person

3
@GoodPerson nl은 기본적으로 빈 줄에 번호를 매기 지 않습니다. 이 -ba옵션은 보편적이지 않은 일부 시스템에서 사용할 수 있습니다 (HP / UX는 마음에 들지 않지만) (이 경우에는 비어 있음) 정규식과 일치하는 모든 줄에 grep -n항상 번호를 매 깁니다.
— ghoti

1
대신 둔한의 I 사용의cut -d: -f2-
— 알렉산더 스텀프

17

그냥 배쉬 :) (4.0+)

function print_reversed {
    local lines i
    readarray -t lines

    for (( i = ${#lines[@]}; i--; )); do
        printf '%s\n' "${lines[i]}"
    done
}

print_reversed < file

2
bash와 O (n)에 대한 답과 재귀를 사용하지 않는 것에 대한 +1 (가능한 경우 +3)
— nhed

2
이 줄을 포함하는 파일로 이것을 시도 -nenenenenenene하고 사람들이 printf '%s\n'대신 항상 사용 하는 것이 좋습니다 이유를 목격하십시오 echo.
— mtraceur

@ mtraceur 이번에는 일반적인 기능이기 때문에 이번에는 동의합니다.
— konsolebox

11

가장 간단한 방법은 tac명령을 사용하는 것 입니다. tac이다 cat의 역수. 예:

$ cat order.txt
roger shah 
armin van buuren
fpga vhdl arduino c++ java gridgain
$ tac order.txt > inverted_file.txt
$ cat inverted_file.txt
fpga vhdl arduino c++ java gridgain
armin van buuren
roger shah 

1
왜이 답변이 아래 답변 앞에 표시되는지 확실하지 않지만 수년 전에 게시 된 stackoverflow.com/a/742485/1174784 의 중복입니다 .
— anarcat

10

나는 " 꼬리 -r "답변을 정말 좋아 하지만 가장 좋아하는 gawk 답변은 ...입니다.

gawk '{ L[n++] = $0 } 
  END { while(n--) 
        print L[n] }' file

mawk우분투 14.04 LTS에서 테스트되었습니다 -작동하므로 GNU awk에 국한되지 않습니다. +1
— Sergiy Kolodyazhnyy

n++NR
— karakfa

3

다음을 편집 하면 1에서 10 사이의 무작위로 정렬 된 숫자 목록이 생성됩니다.

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') **...**

여기서 점은 목록을 반전시키는 실제 명령으로 대체됩니다.

전술

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(tac)

파이썬 : sys.stdin에서 [::-1] 사용

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(python -c "import sys; print(''.join(([line for line in sys.stdin])[::-1]))")

3

tac쉘 스크립트 내부에서 사용할 수있는 교차 OS (예 : OSX, Linux) 솔루션의 경우 위에서 언급 한 것처럼 homebrew를 사용하고 다음과 같이 별칭 전술을 사용하십시오.

lib 설치

MacOS 용

brew install coreutils

리눅스 데비안

sudo apt-get update
sudo apt-get install coreutils 

그런 다음 별칭을 추가하십시오.

echo "alias tac='gtac'" >> ~/.bash_aliases (or wherever you load aliases)
source ~/.bash_aliases
tac myfile.txt

2

이것은 BSD와 GNU 모두에서 작동합니다.

awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename

1

파일을 제자리에 수정하려면 다음을 수행하십시오.

sed -i '1!G;h;$!d' filename

이렇게하면 임시 파일을 만든 다음 원본을 삭제하거나 이름을 바꿀 필요가 없으며 결과는 같습니다. 예를 들면 다음과 같습니다.

$tac file > file2
$sed -i '1!G;h;$!d' file
$diff file file2
$

ephemient 의 답변을 바탕으로, 내가 원했던 것과 거의 같지만 거의하지는 않았습니다.


1

n매우 큰 텍스트 파일 의 마지막 줄을 효율적 으로 얻고 싶습니다 .

내가 시도한 첫 번째 방법은 tail -n 10000000 file.txt > ans.txt이지만 매우 느리다는 것을 알았습니다. 왜냐하면 tail위치를 찾은 다음 뒤로 이동하여 결과를 인쇄해야하기 때문입니다.

그것을 알게되면 다른 솔루션으로 전환합니다 tac file.txt | head -n 10000000 > ans.txt. 이번에는 탐색 위치가 끝에서 원하는 위치로 이동하기 만하면 50 %의 시간이 절약됩니다 !

집에 메시지를 :

옵션 이없는 tac file.txt | head -n n경우 사용하십시오 .tail-r


0

최고의 솔루션 :

tail -n20 file.txt | tac

스택 오버플로에 오신 것을 환영합니다! 이 코드 스 니펫은 문제를 해결할 수 있지만 설명을 포함하면 게시물의 품질을 향상시키는 데 실제로 도움이됩니다. 앞으로 독자에게 질문에 대한 답변을 제공하고 있으며 해당 사람들이 귀하의 코드 제안 이유를 모를 수도 있습니다. 설명 주석으로 코드를 복잡하게 만들지 마십시오. 이렇게하면 코드와 설명의 가독성이 떨어집니다!
— kayess

0

Emacs 사용자 : C-x h(전체 파일 선택) 및을 차례로 클릭 M-x reverse-region합니다. 또한 부품이나 선을 선택하고 되돌릴 때만 작동합니다.


0

흥미로운 아이디어가 많이 있습니다. 그러나 내 생각을 시도하십시오. 여기에 텍스트를 넣으십시오.

레브 | tr '\ n' '~'| 레브 | tr '~' '\ n'

문자 '~'가 파일에 없다고 가정합니다. 이것은 1961 년으로 돌아가는 모든 유닉스 셸에서 작동해야합니다.


-1

나는 같은 질문을했지만 첫 번째 줄 (헤더)이 맨 위에 머물기를 원했습니다. 그래서 awk의 힘을 사용해야했습니다

cat dax-weekly.csv | awk '1 { last = NR; line[last] = $0; } END { print line[1]; for (i = last; i > 1; i--) { print line[i]; } }'

PS는 cygwin 또는 gitbash에서도 작동합니다.


1\n20\n19...2\n그보다 는 오히려 결과로 나타납니다 20\n19...\2\n1\n.
— Mark Booth

-1

당신은 그것을 할 수 vim stdin와 stdout. 당신은 또한 사용할 수 있습니다 ex로 POSIX를 준수 . vim에 대한 시각적 모드입니다 ex. 실제로 또는 (개선 된 모드) ex와 함께 사용할 수 있습니다 . 같은 도구와 달리 편집을 위해 파일을 버퍼링하고 스트림에 사용 되기 때문에 유용 합니다. 을 사용할 수 있지만 변수의 모든 항목을 수동으로 버퍼링해야합니다.vim -evim -Eexvimsedsedawk

아이디어는 다음을 수행하는 것입니다.

  1. stdin에서 읽기
  2. 각 줄에 대해 줄 1로 이동하십시오 (역순). 명령은 g/^/m0입니다. 이것은 각 라인에 대해 전 세계적으로 의미합니다 g. 줄의 시작과 일치합니다 ^. 주소 1 (1 행) 다음으로 이동하십시오 m0.
  3. 모든 것을 인쇄하십시오. 명령은 %p입니다. 이것은 모든 라인의 범위를 의미합니다 %. 선을 인쇄하십시오 p.
  4. 파일을 저장하지 않고 강제 종료합니다. 명령은 q!입니다. 이것은 종료를 의미합니다 q. 강력하게 !.
# Generate a newline delimited sequence of 1 to 10
$ seq 10
1
2
3
4
5
6
7
8
9
10

# Use - to read from stdin.
# vim has a delay and annoying 'Vim: Reading from stdin...' output
# if you use - to read from stdin. Use --not-a-term to hide output.
# --not-a-term requires vim 8.0.1308 (Nov 2017)
# Use -E for improved ex mode. -e would work here too since I'm not
# using any improved ex mode features.
# each of the commands I explained above are specified with a + sign
# and are run sequentially.
$ seq 10 | vim - --not-a-term -Es +'g/^/m0' +'%p' +'q!'
10
9
8
7
6
5
4
3
2
1
# non improved ex mode works here too, -e.
$ seq 10 | vim - --not-a-term -es +'g/^/m0' +'%p' +'q!'

# If you don't have --not-a-term, use /dev/stdin
seq 10 | vim -E +'g/^/m0' +'%p' +'q!' /dev/stdin

# POSIX compliant (maybe)
# POSIX compliant ex doesn't allow using + sign to specify commands.
# It also might not allow running multiple commands sequentially.
# The docs say "Implementations may support more than a single -c"
# If yours does support multiple -c
$ seq 10 | ex -c "execute -c 'g/^/m0' -c '%p' -c 'q!' /dev/stdin

# If not, you can chain them with the bar, |. This is same as shell
# piping. It's more like shell semi-colon, ;.
# The g command consumes the |, so you can use execute to prevent that.
# Not sure if execute and | is POSIX compliant.
seq 10 | ex -c "execute 'g/^/m0' | %p | q!" /dev/stdin

이것을 재사용 할 수있게 만드는 방법

vedvim sed을 사용하여 편집 할 스크립트 (예 : vim editor )를 사용 stdin합니다. 이것을 ved경로 에있는 파일에 추가하십시오 :

#!/usr/bin/env sh

vim - --not-a-term -Es "$@" +'%p | q!'

vim이 10 개의 명령으로 제한하기 때문에, +대신 하나의 명령을 사용하고 +'%p' +'q!'있습니다. 따라서 그것들을 병합하면 8 대신 "$@"9 개의 +명령 을 가질 수 있습니다 .

그럼 당신은 할 수 있습니다 :

seq 10 | ved +'g/^/m0'

vim 8이 없다면 ved대신 이것을 넣으십시오 :

#!/usr/bin/env sh

vim -E "$@" +'%p | q!' /dev/stdin

-3
rev
text here

또는

rev <file>

또는

rev texthere

안녕하세요, Stack Overflow에 오신 것을 환영합니다! 질문에 대답 할 때 저자가 잘못한 것과 수정 한 것과 같은 일종의 설명을 포함해야합니다. 귀하의 답변이 품질이 낮은 것으로 표시되어 현재 검토 중이므로이 정보를 알려드립니다. "편집"버튼을 클릭하여 답변을 편집 할 수 있습니다 .
— Federico Grandi

Esp. 오래되고 정답이 높은 질문에 대한 새로운 답변에는 또 다른 답변을 추가 할 충분한 근거가 필요합니다.
— Gert Arnold

rev는 텍스트를 가로로 뒤집어 원하는 동작이 아닙니다.
— D3l_Gato

-4

tail -r 은 대부분의 Linux 및 MacOS 시스템에서 작동

1 1 20 | 꼬리 -r


-9
sort -r < filename

또는

rev < filename

7
sort -r입력이 이미 정렬 된 경우에만 작동하지만 여기에는 해당되지 않습니다. rev줄당 문자를 바꾸지 만 줄 순서는 그대로 유지합니다. 이는 Scotty가 요청한 내용이 아닙니다. 따라서이 답변은 실제로 전혀 답이 아닙니다.
— Alexander Stumpf
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.