我有一個文本文件,有很多行。我還按照特定順序選擇了要打印的行數。比方說,例如,「5,3,10,6」。按此順序。從文件中按特定順序選擇某些行的簡單方法
有沒有一些容易和「規範」的方式來做到這一點? (與「標準」的Linux工具,和bash)
當我試圖從這個問題
Bash tool to get nth line from a file
它總是打印,以便他們在文件中的行的答案。
我有一個文本文件,有很多行。我還按照特定順序選擇了要打印的行數。比方說,例如,「5,3,10,6」。按此順序。從文件中按特定順序選擇某些行的簡單方法
有沒有一些容易和「規範」的方式來做到這一點? (與「標準」的Linux工具,和bash)
當我試圖從這個問題
Bash tool to get nth line from a file
它總是打印,以便他們在文件中的行的答案。
一個相當有效的方法,如果你的文件不是太大就是把它全部讀到內存中,在一個數組中,每場使用一行使用mapfile
(這是Bash≥4內建):
mapfile -t array < file.txt
然後你可以重複你以任意順序想要的線路,例如,
printf '%s\n' "${array[4]}" "${array[2]}" "${array[9]}" "${array[5]}"
打印線5,3,10,6。現在你會覺得這是一個有點尷尬的是,數組字段以0
開頭,以便您必須抵消數字。
mapfile -t -O 1 array < file.txt
這將啓動索引1分配給array
,這樣就可以打印線5,3,10和6:
-O
選項
mapfile
很容易治癒
printf '%s\n' "${array[5]}" "${array[3]}" "${array[10]}" "${array[6]}"
最後,要彌補這方面的包裝功能:
printlines() {
local i
for i; do printf '%s\n' "${array[i]}"; done
}
,讓你ç一個剛剛的狀態:
printlines 5 3 10 6
而且它都是純Bash,沒有外部工具!
由於@glennjackmann表明在評論你可以做輔助功能也需要讀取文件護理(如參數傳遞):
printlinesof() {
# $1 is filename
# $2,... are the lines to print
local i array
mapfile -t -O 1 array < "$1" || return 1
shift
for i; do printf '%s\n' "${array[i]}"; done
}
然後你可以使用它作爲:
printlinesof file.txt 5 3 10 6
如果你也想標準輸入處理:
printlinesof() {
# $1 is filename or - for stdin
# $2,... are the lines to print
local i array file=$1
[[ $file = - ]] && file=/dev/stdin
mapfile -t -O 1 array < "$file" || return 1
shift
for i; do printf '%s\n' "${array[i]}"; done
}
使
printf '%s\n' {a..z} | printlinesof - 5 3 10 6
也會起作用。
這裏有一種方法用awk:
awk -v s='5,3,10,6' 'BEGIN{split(s, a, ","); for (i=1; i<=length(a); i++) b[a[i]]=i}
b[NR]{data[NR]=$0} END{for (i=1; i<=length(a); i++) print data[a[i]]}' file
測試:
cat file
Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
Line 11
Line 12
awk -v s='5,3,10,6' 'BEGIN{split(s, a, ","); for (i=1; i<=length(a); i++) b[a[i]]=i}
b[NR]{data[NR]=$0} END{for (i=1; i<=length(a); i++) print data[a[i]]}' file
Line 5
Line 3
Line 10
Line 6
首先,生成一個sed表達式將在開頭打印帶有數字的行,以後可以使用它對輸出進行排序:
#!/bin/bash
lines=(5 3 10 6)
sed=''
i=0
for line in "${lines[@]}" ; do
sed+="${line}s/^/$((i++)) /p;"
done
for i in {a..z} ; do echo $i ; done \
| sed -n "$sed" \
| sort -n \
| cut -d' ' -f2-
我可能是用Perl,雖然:
for c in {a..z} ; do echo $c ; done \
| perl -e 'undef @lines{@ARGV};
while (<STDIN>) {
$lines{$.} = $_ if exists $lines{$.};
}
print @lines{@ARGV};
' 5 3 10 6
您還可以使用的,而不是在第一個解決方案的sed黑客的Perl:
for c in {a..z} ; do echo $c ; done \
| perl -e ' %lines = map { $ARGV[$_], ++$i } 0 .. $#ARGV;
while (<STDIN>) {
print "$lines{$.} $_" if exists $lines{$.};
}
' 5 3 10 6 | sort -n | cut -d' ' -f2-
l=(5 3 10 6)
printf "%s\n" {a..z} |
sed -n "$(printf "%d{=;p};" "${l[@]}")" |
paste - - | {
while IFS=$'\t' read -r nr text; do
line[nr]=$text
done
for n in "${l[@]}"; do
echo "${line[n]}"
done
}
可以使用nl
招:數字線路輸入,並加入與實際線路號碼列表輸出。需要額外的排序,使join
可能的,因爲它需要排序的輸入(所以nl
訣竅是再次使用數量預計行):
#! /bin/bash
LINES=(5 3 10 6)
lines=$(IFS=$'\n' ; echo "${LINES[*]}" | nl)
for c in {a..z} ; do
echo $c
done | nl \
| grep -E '^\s*('"$(IFS='|' ; echo "${LINES[*]}")"')\s' \
| join -12 -21 <(echo "$lines" | sort -k2n) - \
| sort -k2n \
| cut -d' ' -f3-
+1非常好。 'mapfile'需要bash v4。我會通過傳遞文件名並在函數中執行mapfile來增強它:'printlines(){local i array; mapfile -t -O 1數組<「$ 1」;轉移;爲我;做printf'%s \ n'「$ {array [i]}」;完成; }; printlines file.txt 5 3 10 6' –
我最喜歡這個答案,即使它在文件太大時沒有「縮放」。 –