簡體   English   中英

sed中的命令替換

[英]Command substitution in sed

我想將一個文本文件的前六個字符讀入一個字符串,並在該文件中使用該字符串添加其他所有非空行。 這種文件的一個例子可能是:

04/17 Walmart .toys $ 70 .cash $ -70

Caltex .gas 20 $ .cheque $ -20

McDonalds .burger 1 $ .cash $ -1

每個條目,即:每個非空行,都需要一個日期,出於簡單數據輸入的原因,該日期僅在第一行輸入。 條目由1個或多個空行分隔。 輸出看起來像這樣:

04/17 Walmart .toys $ 70 .cash $ -70

04/17 Caltex .gas 20 $ .cheque $ -20

04/17 McDonalds .burger 1 $ .cash $ -1

我可以將非空字符串與^ [^ @] + [] *之類的東西相匹配 [] ([^; {}:] +)[] * $ ,但我不知道如何為非空行實際實現。

這個Bash腳本對我很有吸引力,但我不知道如何在開始時插入我的字符串。

我也找不到Stack Overflow問題的直接答案。

我嘗試了一個接受文件名的腳本:

read -n 6 date < $1
sed 's/^/$(echo $date)/' | \
sed 's/^$(echo $date)\n//' | > $newName

我能夠想出將日期與空格(例如字符串:'04 / 17')前置到每一行,然后從每行中沒有任何內容刪除它。

但是,似乎sed不接受命令替換:

sed: -e expression #1, char 10: unknown option to `s'

您應該可以使用一個sed命令執行此操作:

read -rn 6 date < "$1"
sed -E 's#^([a-zA-Z]+)#'"$date"' \1#g' "$1" > newfile

捕獲組確保在插入日期之前該行上至少有一個字符。

編輯 :根據您的問題的修訂:

newfile="output.txt"
lineone=$(head -1 "$1");

read -rn 6 date <<< "$lineone"
sed -E 's#^([a-zA-Z]+)#'"$date"' \1#g; 1s#^.*$#'"$lineone"'#' "$1" > "$newfile" 

由於您沒有進行就地編輯,您可以執行$ date插入,然后返回並換出第一行,因為它最終會有兩個日期。 可能有“更好”的方法來執行此操作,例如使用Perl或丟失第二個sed命令,盡管這至少應該為您提供一個基本的想法,但它是如何工作的...

結果 (newfile):

04/17 Walmart .toys $ 70 .cash $ -70

04/17 Caltex .gas 20 $ .cheque $ -20

04/17 McDonalds .burger 1 $ .cash $ -1

注意:在某些版本的sed中,擴展正則表達式的選項可以是-r-E

使用Perl:

perl -plE 'if($.==1){$d=substr($_,0,6);next}elsif(/./){s/^/$d/}' file > new

產量

04/17 Walmart .toys $ 70 .cash $ -70

04/17 Caltex .gas 20 $ .cheque $ -20

04/17 McDonalds .burger 1 $ .cash $ -1

或者在備份到file.bak同一文件中

perl -i.bak -plE 'if($.==1){$d=substr($_,0,6);next}elsif(/./){s/^/$d/}' file

或者沒有備份的同一文件

perl -i -plE 'if($.==1){$d=substr($_,0,6);next}elsif(/./){s/^/$d/}' file

或者,如果您不確定日期中的前導零,

perl -plE 'if($.==1){($d)=m|^(\d+/\d+\s)|;next}elsif(/./){s/^/$d/}' file

將匹配第一行開頭的任何digit(s) / digit(s) space

作為歐萊雅L'升在評論中提及,上述增加了偽空行太(其中線不僅看起來類似空)例如,它至少包含一個空格的日期。 在這種情況下,而不是/./

  • 使用/\\w/ - 所以,只將日期添加到包含至少一個單詞字符的行;
  • 或者在包含至少一個非空格字符時使用/\\S/ -

說明:

perl -plE '                    # Run the commands on every input line and print them.
    if( $. == 1) {             # If it is the 1st line
        $d = substr($_, 0, 6); # take the first 6 characters and store it to $d
        next                   # And continue to the next line.
    }
    elsif( /\S/ ) {            # Else if the line contains any nonspace character
        s/^/$d/                # add to the beginning the content of $d
    }
    ' file > new

純粹的bash回答:

unset n
while read -r x ; do
    case "${#n}$x" in 6) ;; 6*) x="$n$x" ;; *) n="${x:0:6}" ;; esac
    echo "$x"
done < file > newfile

輸出:

04/17 Walmart .toys $ 70 .cash $ -70

04/17 Caltex .gas 20 $ .cheque $ -20

04/17 McDonalds .burger 1 $ .cash $ -1

斜杠正在終止sed命令,將分隔符更改為其他內容:

"s#^#$(echo $date)#"

你也可以這樣寫:

"s#^#$date#"

但請注意,這種方法通常很脆弱(正如您所發現的那樣),因為您無法將變量視為文字字符串。


基於更新問題的示例,我建議使用單個awk命令進行文本處理。 這樣的東西可以給你樣本輸出:

$ cat file
04/17 Walmart .toys $ 70 .cash $ -70

Caltex .gas 20 $ .cheque $ -20

McDonalds .burger 1 $ .cash $ -1

$ awk 'NR==1{d=$1}NR>1&&NF>0{$0=d" "$0}1' file
04/17 Walmart .toys $ 70 .cash $ -70

04/17 Caltex .gas 20 $ .cheque $ -20

04/17 McDonalds .burger 1 $ .cash $ -1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM