以相反的順序列印文件行上的所有單詞
我不知道如何使我的程式碼適用於更多行。
這是原始文件 t.txt:
Hello Earth Hello Mars
但我得到以下輸出:
Mars Hello Earth Hello
我的預期輸出是這樣的:
Earth Hello Mars Hello
一般來說,我想保持行順序相同,但單詞相反。對於一般情況輸入將是這樣的:
one two four five
預期的輸出是這樣的:
two one five four
我的程式碼如下:
#!/bin/bash text=$(cat $1) arr=($text) al=${#arr[@]} let al="al-1" while (($al >= 0)) do echo -n "${arr[al]}" echo -n " " let al="al - 1" done echo
下面介紹的所有範例都適用於行中有任意數量的單詞的一般情況。基本思想在任何地方都是相同的——我們必須逐行讀取文件並反向列印單詞。AWK 最好地促進了這一點,因為它已經擁有以程式方式完成文本處理的所有必要工具,並且是最便攜的 - 它可以與任何 awk 衍生產品一起使用,並且大多數係統都有它。Python 也有很多很好的字元串處理實用程序,可以讓我們完成這項工作。我想說,它是用於更現代系統的工具。恕我直言,Bash 是最不可取的方法,因為它具有可移植性、潛在危險以及需要完成的“詭計”的數量。
AWK
$ awk '{for(i=NF;i>=1;i--) printf "%s ", $i;print ""}' input.txt Earth Hello Mars Hello
它的工作方式相當簡單:我們在行中的每個單詞中向後循環,列印用空格分隔的單詞 - 這是由
printf "%s ",$i
函式(用於列印格式化字元串)和 for 循環完成的。NF
變數對應於欄位數。預設欄位分隔符假定為空格。我們首先將一個丟棄變數設置i
為單詞數,然後在每次迭代中遞減該變數。因此,如果有 3 個單詞線上,我們列印欄位 $ 3, then $ 2,和 1 美元。在最後一遍之後,變數 i 變為 0,條件i>=1
變為 false,循環終止。為了防止線被拼接在一起,我們使用 . 插入換行符print ""
。在這種情況下,每行都會處理AWK 程式碼塊{}
(如果程式碼塊前面有匹配條件,則取決於要執行的程式碼塊是否匹配)。Python
對於那些喜歡替代解決方案的人,這裡是 python:
$ python -c "import sys;print '\n'.join([ ' '.join(line.split()[::-1]) for line in sys.stdin ])" < input.txt Earth Hello Mars Hello
這裡的想法略有不同。
<
運算符告訴您目前的 shell 重定向input.txt
到 python 的stdin
流,我們逐行讀取該流。在這裡,我們使用列表推導來創建行列表——這就是該[ ' '.join(line.split()[::-1]) for line in sys.stdin ]
部分所做的。該部分' '.join(line.split()[::-1])
取一行,將其拆分為單詞列表,通過 反轉列表[::-1]
,然後從中' '.join()
創建一個空格分隔的字元串。因此,我們有一個更大的字元串列表。最後,'\n'.join()
製作一個更大的字元串,每個項目都通過換行符連接。簡而言之,這種方法基本上是一種“打破並重建”的方法。
重擊
#!/bin/bash while IFS= read -r line do bash -c 'i=$#; while [ $i -gt 0 ];do printf "%s " ${!i}; i=$(($i-1)); done' sh $line echo done < input.txt
和一個測試執行:
$ ./reverse_words.sh Earth Hello Mars Hello
Bash 本身沒有強大的文本處理能力。這裡發生的是我們通過逐行讀取文件
while IFS= read -r line do # some code done < text.txt
這是一種常見的技術,在 shell 腳本中廣泛用於逐行讀取命令或文本文件的輸出。每行都儲存到
$line
變數中。在裡面我們有
bash -c 'i=$#; while [ $i -gt 0 ];do printf "%s " ${!i}; i=$(($i-1)); done' sh $line
在這裡,我們使用
bash
with-c
flag 來執行一組包含在單引號中的命令。使用時-c
,bash
將開始將命令行參數分配給以 . 開頭的變數$0
。因為它$0
傳統上用來表示程序的名稱,所以我sh
首先使用虛擬變數。由於被稱為分詞的行為,未引用的
$line
內容將被分解為單獨的項目。在 shell 腳本中分詞通常是不可取的,你經常會聽到人們說“總是引用你的變數,比如“$foo”。” 然而,在這種情況下,分詞對於處理簡單文本是可取的。如果您的文本包含類似 的內容$var
,則可能會破壞這種方法。為此,以及其他幾個原因,我會說 python 和 awk 方法更好。至於內碼,也很簡單:把不帶引號
$line
的分詞,傳給內碼處理。我們獲取參數的數量$#
,將其儲存到丟棄的變數i
中,然後再次使用稱為變數間接的東西列印出每個項目——這就是${!i}
部分(注意這是 bashism——它在其他 shell 中不可用)。再一次,我們使用printf "%s "
以空格分隔的列印出每個單詞。完成後,echo
將附加一個換行符。本質上,這種方法是 awk 和 python 的混合。我們逐行讀取文件,但分而治之,使用 的幾個
bash
特性來完成這項工作。可以使用 GNU
tac
命令完成更簡單的變體,然後再次使用分詞。tac
用於反轉輸入流或文件的行,但在這種情況下,我們指定-s " "
使用空格作為分隔符。因此,var
將包含一個以換行符分隔的反向單詞列表,但由於$var
沒有被引用,換行符將被空格替換。詭計,同樣不是最可靠的,但有效。#!/bin/bash while IFS= read -r line do var=$(tac -s " " <<< "$line" ) echo $var done < input.txt
測試執行:
這是具有任意輸入行的 3 種方法
$ cat input.txt Hello Earth end of line Hello Mars another end of line abra cadabra magic $ ./reverse_words.sh line of end Earth Hello line of end another Mars Hello magic cadabra abra $ python -c "import sys;print '\n'.join([ ' '.join(line.split()[::-1]) for line in sys.stdin ])" < input.txt line of end Earth Hello line of end another Mars Hello magic cadabra abra $ awk '{for(i=NF;i>=1;i--) printf "%s ", $i;print ""}' input.txt line of end Earth Hello line of end another Mars Hello magic cadabra abra
額外:perl 和 ruby
與 python 相同的想法 - 我們將每一行拆分為單詞數組,反轉數組並將其列印出來。
$ perl -lane '@r=reverse(@F); print "@r"' input.txt line of end Earth Hello line of end another Mars Hello magic cadabra abra $ ruby -ne 'puts $_.chomp.split().reverse.join(" ")' < input.txt line of end Earth Hello line of end another Mars Hello magic cadabra abra