Tôi muốn đảo ngược thứ tự các dòng trong tệp văn bản (hoặc stdin), giữ nguyên nội dung của từng dòng.
Vì vậy, tức là bắt đầu bằng:
foo
bar
baz
Tôi muốn kết thúc với
baz
bar
foo
Có một tiện ích dòng lệnh UNIX tiêu chuẩn cho việc này không?
Tôi muốn đảo ngược thứ tự các dòng trong tệp văn bản (hoặc stdin), giữ nguyên nội dung của từng dòng.
Vì vậy, tức là bắt đầu bằng:
foo
bar
baz
Tôi muốn kết thúc với
baz
bar
foo
Có một tiện ích dòng lệnh UNIX tiêu chuẩn cho việc này không?
Câu trả lời:
Cũng đáng đề cập: tac
(the, ahem, đảo ngược của cat
). Một phần của coreutils .
tac a.txt > b.txt
brew install coreutils
(cài đặt gtac
theo mặc định).
echo -n "abc\ndee" > test; tac test
.
Có những thủ thuật sed nổi tiếng :
# 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
(Giải thích: thêm dòng không phải ban đầu để giữ bộ đệm, dòng trao đổi và giữ bộ đệm, in dòng ở cuối)
Ngoài ra (với thực thi nhanh hơn) từ các lớp lót awk :
awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file*
Nếu bạn không thể nhớ điều đó,
perl -e 'print reverse <>'
Trên một hệ thống có các tiện ích GNU, các câu trả lời khác đơn giản hơn, nhưng không phải tất cả thế giới là GNU / Linux ...
tail -r
và tac.
tac
dự kiến sẽ xử lý các tệp lớn tùy ý không phù hợp với bộ nhớ (mặc dù độ dài dòng vẫn bị giới hạn). Không rõ liệu sed
giải pháp làm việc cho các tập tin như vậy.
ở cuối lệnh của bạn đặt:
| tac
tac thực hiện chính xác những gì bạn yêu cầu, đó là "Viết từng TẬP TIN vào đầu ra tiêu chuẩn, dòng cuối cùng trước."
tac là đối diện của con mèo :-).
tac
lệnh, điều này hữu ích cho những người dùng mới có thể cuối cùng tìm kiếm cùng một chủ đề.
Nếu bạn đang vim
sử dụng
:g/^/m0
$ (tac 2> /dev/null || tail -r)
Hãy thử tac
, hoạt động trên Linux và nếu nó không hoạt động tail -r
, nó hoạt động trên BSD và OSX.
tac myfile.txt
- tôi đang thiếu gì?
tail -r
trong trường hợp tac
không có sẵn. tac
không tuân thủ POSIX. Cũng không tail -r
. Vẫn không thể đánh lừa, nhưng điều này giúp cải thiện tỷ lệ làm việc.
tac
có sẵn, nhưng hết RAM và trao đổi một nửa thông qua việc tiêu thụ một luồng đầu vào khổng lồ. Nó thất bại và sau đó tail -r
thành công trong việc xử lý phần còn lại của luồng cho kết quả không chính xác.
brew install coreutils
và sử dụng gtac
thay thế tac
và nếu bạn thích thêm tac làm bí danh gtac
nếu ví dụ bạn muốn một tập lệnh shell sử dụng nó đa nền tảng (Linux, OSX)
Hãy thử lệnh sau:
grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"
sed 's/^[0-9]*://g'
nl
theo mặc định sẽ không đánh số dòng trống. Các -ba
tùy chọn có sẵn trên một số hệ thống, chứ không phải là không phổ biến (HP / UX nói đến cái tâm, mặc dù tôi muốn nó sẽ không) trong khi grep -n
sẽ luôn số mỗi dòng phù hợp với regex (trong trường hợp này có sản phẩm nào).
cut -d: -f2-
Chỉ cần 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
và chứng kiến lý do tại sao mọi người khuyên bạn nên luôn luôn sử dụng printf '%s\n'
thay vì echo
.
Phương pháp đơn giản nhất là sử dụng tac
lệnh. tac
là cat
nghịch đảo. Thí dụ:
$ 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
Tôi thực sự thích câu trả lời " đuôi -r ", nhưng câu trả lời yêu thích của tôi là ....
gawk '{ L[n++] = $0 }
END { while(n--)
print L[n] }' file
mawk
Ubuntu 14.04 LTS - hoạt động, vì vậy nó không phải là GNU awk cụ thể. +1
n++
có thể được thay thế bằngNR
EDIT sau đây tạo ra một danh sách các số được sắp xếp ngẫu nhiên từ 1 đến 10:
seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') **...**
nơi các dấu chấm được thay thế bằng lệnh thực tế đảo ngược danh sách
tấc
seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(tac)
python: sử dụng [:: - 1] trên 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]))")
Đối với giải pháp hệ điều hành chéo (ví dụ OSX, Linux) có thể sử dụng tac
bên trong tập lệnh shell, sử dụng homebrew như những người khác đã đề cập ở trên, sau đó chỉ cần bí danh như vậy:
Cài đặt lib
Dành cho MacOS
brew install coreutils
Dành cho linux debian
sudo apt-get update
sudo apt-get install coreutils
Sau đó thêm bí danh
echo "alias tac='gtac'" >> ~/.bash_aliases (or wherever you load aliases)
source ~/.bash_aliases
tac myfile.txt
Nếu bạn muốn sửa đổi tập tin tại chỗ, bạn có thể chạy
sed -i '1!G;h;$!d' filename
Điều này loại bỏ sự cần thiết phải tạo một tệp tạm thời và sau đó xóa hoặc đổi tên ban đầu và có kết quả tương tự. Ví dụ:
$tac file > file2
$sed -i '1!G;h;$!d' file
$diff file file2
$
Dựa trên câu trả lời của ephemient , đã làm gần như, nhưng không hoàn toàn, những gì tôi muốn.
Nó xảy ra với tôi rằng tôi muốn có được những n
dòng cuối cùng của một tệp văn bản rất lớn một cách hiệu quả .
Điều đầu tiên tôi đã thử là tail -n 10000000 file.txt > ans.txt
, nhưng tôi thấy nó rất chậm, vìtail
phải tìm đến vị trí và sau đó di chuyển trở lại để in kết quả.
Khi tôi nhận ra nó, tôi chuyển sang một giải pháp khác : tac file.txt | head -n 10000000 > ans.txt
. Lần này, vị trí tìm kiếm chỉ cần di chuyển từ cuối đến vị trí mong muốn và nó tiết kiệm 50% thời gian !
Tin nhắn về nhà:
Sử dụng tac file.txt | head -n n
nếu bạn tail
không có -r
tùy chọn.
Giải pháp tốt nhất:
tail -n20 file.txt | tac
Đối với người dùng Emacs: C-x h
(chọn toàn bộ tệp) và sau đó M-x reverse-region
. Cũng hoạt động chỉ để chọn các bộ phận hoặc các dòng và hoàn nguyên chúng.
Tôi thấy rất nhiều ý tưởng thú vị. Nhưng hãy thử ý tưởng của tôi. Đưa văn bản của bạn vào đây:
vòng quay | tr '\ n' '~' | vòng quay | tr '~' '\ n'
giả định rằng ký tự '~' không có trong tệp. Điều này sẽ hoạt động trên mọi shell UNIX từ năm 1961. Hoặc một cái gì đó tương tự.
Tôi đã có cùng một câu hỏi, nhưng tôi cũng muốn dòng đầu tiên (tiêu đề) luôn ở trên đầu. Vì vậy, tôi cần phải sử dụng sức mạnh của 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 cũng hoạt động trong cygwin hoặc gitbash
1\n20\n19...2\n
hơn là 20\n19...\2\n1\n
.
Bạn có thể làm điều đó với vim
stdin
và stdout
. Bạn cũng có thể sử dụng ex
để tuân thủ POSIX . vim
chỉ là chế độ trực quan cho ex
. Trong thực tế, bạn có thể sử dụng ex
với vim -e
hoặc vim -E
( ex
chế độ cải tiến ).
vim
là hữu ích vì không giống như các công cụ như sed
nó đệm tệp để chỉnh sửa, trong khi sed
được sử dụng cho các luồng. Bạn có thể sử dụngawk
, nhưng bạn sẽ phải tự đệm mọi thứ trong một biến.
Ý tưởng là để làm như sau:
g/^/m0
. Điều này có nghĩa là trên toàn cầu, cho mỗi dòng g
; phù hợp với sự bắt đầu của dòng, phù hợp với bất cứ điều gì ^
; di chuyển nó sau địa chỉ 0, đó là dòng 1m0
.%p
. Điều này có nghĩa cho phạm vi của tất cả các dòng %
; in dòng p
.q!
. Điều này có nghĩa là bỏ thuốc lá q
; mạnh mẽ !
.# 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
Làm thế nào để làm điều này tái sử dụng
Tôi sử dụng một tập lệnh mà tôi gọi ved
(vim biên tập như sed
) để sử dụng vim để chỉnh sửa stdin
. Thêm phần này vào một tệp được gọi ved
trong đường dẫn của bạn:
#!/usr/bin/env sh
vim - --not-a-term -Es "$@" +'%p | q!'
Tôi đang sử dụng một +
lệnh thay vì +'%p' +'q!'
, vì vim giới hạn bạn 10 lệnh. Vì vậy, hợp nhất chúng cho phép "$@"
có 9+
lệnh thay vì 8.
Sau đó, bạn có thể làm:
seq 10 | ved +'g/^/m0'
Nếu bạn không có vim 8, ved
thay vào đó , hãy đặt cái này vào :
#!/usr/bin/env sh
vim -E "$@" +'%p | q!' /dev/stdin
rev
text here
hoặc là
rev <file>
hoặc là
rev texthere
sort -r < filename
hoặc là
rev < filename
sort -r
chỉ hoạt động nếu đầu vào đã được sắp xếp, đó không phải là trường hợp ở đây. rev
đảo ngược các ký tự trên mỗi dòng nhưng vẫn giữ nguyên thứ tự dòng đó cũng không phải là thứ mà Scotty yêu cầu. Vì vậy, câu trả lời này thực sự không có câu trả lời nào cả.
perl -e 'print reverse <>'
nhưng có lẽ nó cũng áp dụng cho các phương thức khác).