Bash

以相反的順序列印文件行上的所有單詞

  • March 4, 2021

我不知道如何使我的程式碼適用於更多行。

這是原始文件 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

在這裡,我們使用bashwith -cflag 來執行一組包含在單引號中的命令。使用時-cbash將開始將命令行參數分配給以 . 開頭的變數$0。因為它$0傳統上用來表示程序的名稱,所以我sh首先使用虛擬變數。

由於被稱為分詞的行為,未引用的$line內容將被分解為單獨的項目。在 shell 腳本中分詞通常是不可取的,你經常會聽到人們說“總是引用你的變數,比如“$foo”。” 然而,在這種情況下,分詞對於處理簡單文本是可取的。如果您的文本包含類似 的內容$var,則可能會破壞這種方法。為此,以及其他幾個原因,我會說 python 和 awk 方法更好。

至於內碼,也很簡單:把不帶引號$line的分詞,傳給內碼處理。我們獲取參數的數量$#,將其儲存到丟棄的變數i中,然後再次使用稱為變數間接的東西列印出每個項目——這就是${!i} 部分(注意這是 bashism——它在其他 shell 中不可用)。再一次,我們使用printf "%s "以空格分隔的列印出每個單詞。完成後,echo將附加一個換行符。

本質上,這種方法是 awk 和 python 的混合。我們逐行讀取文件,但分而治之,使用 的幾個bash特性來完成這項工作。

可以使用 GNUtac命令完成更簡單的變體,然後再次使用分詞。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

引用自:https://unix.stackexchange.com/questions/336149