2017-03-03 60 views
3

我在使用sed編輯日誌文件時遇到了一些麻煩。我已經將它構建到一個函數中,該函數應該用另一個函數的輸出替換兩個搜索字符串之間的文本。它幾乎可以正常工作,但將行按順序打印到日誌文件中。對於我的生活,我無法弄清楚爲什麼,以及我在嘗試修復它時做出的大部分調整實際上都有不太理想的結果。Bash/Sed - 多行sed操作打印行無序

我sed的功能:

log_edit(){ 
"$3" > temp.txt 
sed -i -n "/$1/{ 
:loop 
n 
/$2/!b loop 
x 
r temp.txt 
G 
s/$2/\n\n&/ 
} 
p" "$FILE" 
rm temp.txt 
} 

我使用了「===文字===」分隔爲我的起始和終止字符串一起傳遞給函數,並使用內置的相同功能首先登錄以填充臨時文本文件。
問題發生在與「G」命令相關的地方。它不是將保持模式行附加到字符串的末尾,而是將它附加到字符串的開頭。

原始日誌採樣/所需的輸出:

=== Metech ITAMS Log === 

Metech Recycling 
ITAMS Hardware Report 
Date: Thu Mar 2 08:01:38 PST 2017 
Tech: SP 

=== Manufacturer Information === 

# dmidecode 2.12 
... 

不幸的是,輸出我得到這個樣子的:

=== Manufacturer Information === 

=== Metech ITAMS Log === 

Metech Recycling 
ITAMS Hardware Report 
Date: Fri Mar 3 09:39:02 PST 2017 
Tech: SS 

# dmidecode 2.12 
... 

會有人能夠幫助我理解我在做什麼錯,還是提出修復?這是我的第一個問題,如果需要更多信息,我很樂意提供。提前致謝。

編輯#1:作爲請求調用該函數的代碼片段:

 2) 
      printf "\n" 
      text_prompt "Please enter Tech initials: " 
      set_tech_id 
      text_prompt "Please enter Traveler ID: " 
      set_travel_id 
      mv "$FILE" "$TRAVEL_ID $TECH_INITIALS" 
      FILE="$TRAVEL_ID $TECH_INITIALS" 
      log_edit "=== Metech ITAMS Log ===" \ 
"=== Manufacturer Information ===" "print_header" 
      unset TECH_INITIALS 
      unset TRAVEL_ID 
     ;; 

這是一個菜單功能的一部分,這將是矯枉過正,包括整個事情,要知道,會有幾個不同的開始/停止字符串調用log_edit(儘管所有都遵循=== ===模式),但通常調用不同的函數來填充temp.txt。

編輯2:爲增加透明度,我認爲我應該添加函數被調用$ 3:

print_header(){ #Prints log header. 
print_div "Metech ITAMS Log" 
printf "Metech Recycling\nITAMS Hardware Report\nDate: $(date)\nTech: %s\n" \ 
"$TECH_INITIALS" 
} 

和print_header調用print_div:

print_div(){ #Prints a divider. Required parameter: $1=Text for divider. 
printf "\n=== %s ===\n\n" "$1" 
} 

編輯3:對於問題的清晰,我問題在於$ 2字符串正在寫入temp.txt內容之前的日誌,而不是之後。

最終修改:找到了解決方案。我想我會發佈下面的工作代碼,以防萬一它有助於其他人。我的問題的一大部分是sed如何使用'r'命令的一個誤解。這個解決方案的另一部分來自我接受的答案,但我仍然不明白,那就是替換命令添加反斜槓,這是使其工作的關鍵。我不知道它爲什麼會起作用,但確實如此。

log_edit() { #Works!! 
"$3" > temp.txt 
sed -i -n '/^'"$1"'$/ { 
:loop 
n 
/^'"$2"'$/!b loop 
i\ 
'"$(sed 's/\\/\\&/g;s/$/\\/' -- "temp.txt")"' 

#Blank line terminates i command. 
} 
p' "$FILE" 
rm temp.txt 
} 
+0

你有沒有完全使用'sed'的心? – eewanco

+0

歡迎來到本網站!請[編輯你的問題](https://stackoverflow.com/posts/42585989/edit)也顯示調用'log_edit'的代碼。謝謝! – cxw

+0

只要一般功能保持不變,我就可以選擇其他選擇。只要我可以使用新測試的結果切換日誌的一部分,並保持格式不變,那麼無論它是sed/awk/perl都不重要。不幸的是,我還不知道AWK或perl的語言。 – makraiz

回答

2

r命令複製出該文件的下一個前閱讀,而不是當其被評估,並且不修改模式空間。然而該文件可以被插入到腳本作爲i命令的一部分:

log_edit() { 
    sed -n '/^'"$1"'$/ { 
     p 
     :loop 
     n 
     /^'"$2"'$/!bloop 
     i\ 
'"$("$3" | sed 's/^[[:space:]]/\\&/;s/\\/\\&/g;s/$/\\/')"' 

     # The blank line above is part of the `i' command, 
     # and appends a newline to the inserted text. 
    } 
    p' "$FILE" > "$FILE.mod" && mv -f -- "$FILE.mod" "$FILE" 
} 

命令取代"$("$3" | sed '...')"濾波器輸出的$3 用於與sedi命令使用。 i命令打印 一系列行將除了最後一個結尾與\

$ echo three | sed 'i\ 
> one\ 
> two 
> ' 
one 
two 
three 
+0

這個解決方案最初引發了一個錯誤,但是我可以通過將引用改爲「$ 3」回到臨時文件解決方案來修復它。這比我自己的解決方案有點接近所需的結果,但它不會刪除新數據應該覆蓋的舊數據。我不明白用''$(sed's/\\/\\ &/g; s/$/\\ /' - 「$ 3」)「'行替代命令的目的。你介意解釋一下嗎? – makraiz

+0

隨着一點修改,我能夠得到這個工作產生所需的輸出。我仍然不確定如何添加反斜槓會改變這是否正常工作,但是它正在工作。謝謝! – makraiz

+1

@makraiz我已經改進了答案,並添加了關於使用'i'命令的解釋。 – kdhp

0

根據圖案嘗試csplit程序,它可以將一個文件分成幾個部分:

csplit $3 "/\($1\|$2\)/" "{*}" 

這意味着取文件$3,並將其分解成文件xxNN(其中NN開始於00並且上升)根據由無限數量({*})劃分的部分$1$2$2(兩個交替模式,由\|分隔並且由轉義標準肌腱端)。分界線將保留在輸出中。然後您可以編寫輔助代碼來刪除不需要的文件。您還可以更改輸出文件名和模式的名稱。

# cat foo 
a 
b 
@ 
c 
d 
% 
e 
f 
@ 
g 
h 
% 
i 
j 
# csplit foo '/\(@\|%\)/' '{*}' 
4 
6 
6 
6 
6 
# more xx0* 
:::::::::::::: 
xx00 
:::::::::::::: 
a 
b 
:::::::::::::: 
xx01 
:::::::::::::: 
@ 
c 
d 
:::::::::::::: 
xx02 
:::::::::::::: 
% 
e 
f 
:::::::::::::: 
xx03 
:::::::::::::: 
@ 
g 
h 
:::::::::::::: 
xx04 
:::::::::::::: 
% 
i 
j 

注意:如果您的分界線可能重複/不按順序發生,您需要對其進行調整。這很簡單;無論順序如何,休息都會發生在看到一個圖案或另一個圖案的任何點上。

+0

如果我沒有弄錯,這似乎與我已經用'$ 3> temp.txt'進行的操作完全相同。問題不在於分裂,而在於重新組合日誌的順序。我需要日誌保持原來的格式。爲了增加清晰度,我將編輯我的問題以包含在$ 3中調用的函數(至少對於此特定示例)。 – makraiz

1

看起來只是一些不合適的事情。試試這個:

log_edit(){ 
"$3" > text.tmp 
sed -i -n "/$1/{ 
r text.tmp 
:loop 
N 
/$2/!b loop 
s/.*\n/\n\n/g 
} 
p 
" "$FILE" 
rm text.tmp 
} 

print_header(){ #Prints log header. 
    print_div "Metech ITAMS Log" 
    printf "Metech Recycling\nITAMS Hardware Report\nDate: $(date)\nTech:%s" "$TECH_INITIALS" 
} 

print_div(){ #Prints a divider. Required parameter: $1=Text for divider. 
    printf "\n=== %s ===\n\n" "$1" 
} 

log_edit "=== Metech ITAMS Log ===" "=== Manufacturer Information ===" "print_header" 
+0

這會導致'未終止的s命令(char 70)',儘管s命令對我來說看起來很好。 – makraiz

+1

有趣的是,我能夠運行這個確切的代碼,並得到你正在尋找的結果。我試圖修改您的解決方案以滿足您的需求,或許您的sed版本或所運行的shell與我自己的版本有所不同。 –

+1

我幾乎肯定這是與此有關的某種問題。不知怎的,解決方案包含一個奇怪的替代命令,基本上增加了額外的'\'字符到任何現有的字符得到了這個工作,但我不能解釋爲什麼。我的日誌實際上並不包含任何反斜槓,所以它必須修改看不見的換行符。爲什麼會改變sed的行爲對我來說是個謎。如果您想查看最終爲我工作的解決方案,我確實將我的工作代碼發佈爲我的問題的「最終編輯」。不過謝謝,這些答案都讓我看到了正確的方向。 – makraiz