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


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 , OpenBSDOS 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 stdinstdout. 당신은 또한 사용할 수 있습니다 exPOSIX를 준수 . 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.