ฉันต้องการย้อนกลับลำดับของบรรทัดในไฟล์ข้อความ (หรือ stdin) รักษาเนื้อหาของแต่ละบรรทัด
ดังนั้นคือเริ่มต้นด้วย:
foo
bar
baz
ฉันต้องการที่จะจบลงด้วย
baz
bar
foo
มียูทิลิตี commandline UNIX มาตรฐานสำหรับสิ่งนี้หรือไม่?
ฉันต้องการย้อนกลับลำดับของบรรทัดในไฟล์ข้อความ (หรือ stdin) รักษาเนื้อหาของแต่ละบรรทัด
ดังนั้นคือเริ่มต้นด้วย:
foo
bar
baz
ฉันต้องการที่จะจบลงด้วย
baz
bar
foo
มียูทิลิตี commandline UNIX มาตรฐานสำหรับสิ่งนี้หรือไม่?
คำตอบ:
ควรพูดถึง: tac
(the, ahem, reverse of cat
) เป็นส่วนหนึ่งของcoreutils
tac a.txt > b.txt
brew install coreutils
(ติดตั้งgtac
ตามค่าเริ่มต้น)
echo -n "abc\ndee" > test; tac test
.
มีเทคนิคหลอกลวงที่รู้จักกันดี :
# 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 ...
tail -r
และจะเป็นแบบแทค
tac
คาดว่าจะจัดการกับไฟล์ขนาดใหญ่โดยพลการที่ไม่พอดีกับหน่วยความจำ (ความยาวบรรทัดยัง จำกัด อยู่) มันไม่ชัดเจนว่าsed
การแก้ปัญหาทำงานได้กับไฟล์ดังกล่าว
ในตอนท้ายของคำสั่งของคุณใส่:
| tac
แทคทำสิ่งที่คุณต้องการอย่างแน่นอน "เขียนไฟล์แต่ละไฟล์ไปยังเอาต์พุตมาตรฐานบรรทัดสุดท้ายก่อน"
tac อยู่ตรงข้ามกับ cat :-)
tac
คำสั่งซึ่งมีประโยชน์สำหรับผู้ใช้ใหม่ที่อาจค้นหาเรื่องเดียวกัน
หากคุณบังเอิญเกิดการvim
ใช้งาน
:g/^/m0
tac <file_name>
ตัวอย่าง:
$ cat file1.txt
1
2
3
4
5
$ tac file1.txt
5
4
3
2
1
$ (tac 2> /dev/null || tail -r)
ลองใช้tac
งานได้กับ Linux และหากใช้ไม่ได้ผลtail -r
จะทำงานกับ BSD และ OSX
tac myfile.txt
- ฉันหายไปอะไร
tail -r
ในกรณีที่tac
ไม่พร้อมใช้งาน tac
ไม่เป็นไปตาม POSIX tail -r
ไม่เป็น ยังคงไม่สามารถป้องกันความผิดพลาดได้ แต่จะช่วยเพิ่มโอกาสในการทำงาน
tac
มี แต่จะหมด RAM และสลับไปมาครึ่งทางผ่านการสตรีมอินพุตขนาดใหญ่ มันล้มเหลวและจากนั้นก็tail -r
ประสบความสำเร็จในการประมวลผลส่วนที่เหลือของกระแสให้ผลลัพธ์ที่ไม่ถูกต้อง
brew install coreutils
และการใช้งานgtac
ในสถานที่tac
และถ้าคุณชอบแทคเพิ่มเป็นนามแฝงไปgtac
ถ้าเช่นคุณอยากเชลล์สคริปต์ที่ใช้มันข้ามแพลตฟอร์ม (Linux, OSX)
ลองคำสั่งต่อไปนี้:
grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"
sed 's/^[0-9]*://g'
nl
โดยค่าเริ่มต้นจะไม่สามารถนับจำนวนบรรทัดว่างได้ -ba
ตัวเลือกที่มีอยู่ในบางระบบไม่ได้ไม่เป็นสากล (HP / UX มาถึงใจ แต่ฉันหวังว่ามันจะไม่ได้) ในขณะที่grep -n
จะจำนวนเสมอทุกสายที่ตรงกับ (ในกรณีนี้ว่าง) regex
cut -d: -f2-
Just Bash :) (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
-nenenenenenene
และเป็นสักขีพยานเหตุผลที่คนขอแนะนำให้ใช้แทนprintf '%s\n'
echo
วิธีที่ง่ายที่สุดคือการใช้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
ฉันชอบคำตอบ" tail -r " จริงๆแต่คำตอบที่ชอบที่สุดของฉันคือ ....
gawk '{ L[n++] = $0 }
END { while(n--)
print L[n] }' file
mawk
Ubuntu 14.04 LTS - ใช้งานได้จึงไม่เฉพาะ GNU awk +1
n++
สามารถแทนที่ด้วยNR
แก้ไข ข้อมูลต่อไปนี้สร้างรายการเรียงลำดับหมายเลขสุ่มจาก 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)
python: การใช้ [:: - 1] บน sys.stdin
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]))")
สำหรับโซลูชันข้าม OS (เช่น OSX, Linux) ที่อาจใช้tac
ในเชลล์สคริปต์ใช้ homebrew ตามที่คนอื่น ๆ ได้กล่าวถึงข้างต้นแล้วเพียงแค่นามแฝงเช่นนั้น:
ติดตั้ง lib
สำหรับ MacOS
brew install coreutils
สำหรับ linux debian
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
สิ่งนี้จะใช้ได้ทั้ง BSD และ GNU
awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename
หากคุณต้องการแก้ไขไฟล์ในสถานที่คุณสามารถเรียกใช้
sed -i '1!G;h;$!d' filename
สิ่งนี้จะลบความจำเป็นในการสร้างไฟล์ชั่วคราวจากนั้นลบหรือเปลี่ยนชื่อต้นฉบับและมีผลลัพธ์เหมือนกัน ตัวอย่างเช่น:
$tac file > file2
$sed -i '1!G;h;$!d' file
$diff file file2
$
ตามคำตอบของ ephemientซึ่งทำเกือบ แต่ไม่มากสิ่งที่ฉันต้องการ
มันเกิดขึ้นกับผมว่าผมต้องการที่จะได้รับที่ผ่านมา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
ตัวเลือก
ทางออกที่ดีที่สุด:
tail -n20 file.txt | tac
สำหรับผู้ใช้ Emacs: C-x h
(เลือกไฟล์ทั้งหมด) M-x reverse-region
แล้ว ยังใช้งานได้เฉพาะการเลือกชิ้นส่วนหรือเส้นและคืนค่าเหล่านั้น
ฉันเห็นแนวคิดที่น่าสนใจมากมาย แต่ลองคิดดูสิ วางข้อความของคุณลงในส่วนนี้:
rev | tr '\ n' '~' | rev | tr '~' '\ n'
ซึ่งถือว่าอักขระ '~' ไม่ได้อยู่ในไฟล์ สิ่งนี้น่าจะใช้ได้กับยูนิกซ์ทุกเชลล์ที่จะย้อนกลับไปในปี 1961 หรืออะไรทำนองนั้น
ฉันมีคำถามเดียวกัน แต่ฉันต้องการให้บรรทัดแรก (ส่วนหัว) อยู่ด้านบน ดังนั้นฉันต้องใช้พลังแห่ง 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
คุณสามารถทำมันได้ด้วยและvim
stdin
stdout
นอกจากนี้คุณยังสามารถใช้ex
ให้เป็นตาม POSIX เป็นเพียงโหมดภาพสำหรับvim
ex
ที่จริงแล้วคุณสามารถใช้ex
กับvim -e
หรือvim -E
( ex
โหมดที่ได้รับการปรับปรุง)
vim
มีประโยชน์เพราะไม่เหมือนเครื่องมือที่ชอบsed
บัฟเฟอร์ไฟล์สำหรับการแก้ไขในขณะที่sed
ใช้สำหรับสตรีม คุณอาจจะสามารถใช้งานawk
ได้ แต่คุณจะต้องบัฟเฟอร์ทุกอย่างด้วยตนเองในตัวแปร
ความคิดคือการทำดังต่อไปนี้:
g/^/m0
คำสั่ง ซึ่งหมายความว่าทั่วโลกสำหรับแต่ละบรรทัดg
; ตรงกับจุดเริ่มต้นของสายซึ่งตรงกับอะไร^
; ย้ายที่อยู่หลัง 0 ซึ่งเป็นสาย m0
1%p
คำสั่ง ซึ่งหมายความว่าสำหรับช่วงของทุกสาย%
; p
พิมพ์บรรทัด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
วิธีที่จะทำให้นี้นำมาใช้ใหม่
ผมใช้โทรสคริปต์ฉันved
(แก้ไขเป็นกลุ่มเช่นsed
) stdin
เพื่อใช้เป็นกลุ่มที่จะแก้ไข เพิ่มไปยังไฟล์ที่เรียกว่าved
ในเส้นทางของคุณ:
#!/usr/bin/env sh
vim - --not-a-term -Es "$@" +'%p | q!'
ฉันใช้+
คำสั่งเดียวแทน+'%p' +'q!'
เพราะ vim จำกัด คุณไว้ที่ 10 คำสั่ง ดังนั้นการรวมพวกเขาอนุญาตให้"$@"
มี 9 +
คำสั่งแทน 8
จากนั้นคุณสามารถทำได้:
seq 10 | ved +'g/^/m0'
หากคุณไม่มีเสียงเรียก 8 ให้ใส่มันved
แทน:
#!/usr/bin/env sh
vim -E "$@" +'%p | q!' /dev/stdin
rev
text here
หรือ
rev <file>
หรือ
rev texthere
tail -rทำงานในระบบ Linux และ MacOS ส่วนใหญ่
seq 1 20 | หาง -r
sort -r < filename
หรือ
rev < filename
sort -r
ใช้งานได้เฉพาะถ้าอินพุตถูกเรียงลำดับแล้วซึ่งไม่ใช่กรณีที่นี่ rev
ฝืนตัวละครต่อบรรทัด แต่รักษาคำสั่งซื้อสายเหมือนเดิมซึ่งก็ไม่ใช่สิ่งที่ Scotty ขอ ดังนั้นคำตอบนี้จริงๆแล้วไม่มีคำตอบเลย
perl -e 'print reverse <>'
แต่อาจใช้กับวิธีอื่นด้วย)