2015-04-28 63 views
2

我得到的文件反覆包含字符串\n\n},我需要用\n}(刪除兩條換行符之一)替換這樣的字符串。
因爲這些文件是通過bash腳本動態生成的,所以我需要在腳本中嵌入替換代碼。Bash替換文件中的' n n}'字符串

我試着用下面的命令,但它不工作:

cat file.tex | sed -e 's/\n\n}/\n}/g' # it doesn't work! 
cat file.tex | perl -p00e 's/\n\n}/\n}/g' # it doesn't work! 
cat file.tex | awk -v RS="" '{gsub (/\n\n}/, "\nb")}1' # it does work, but not for large files 
+1

[沒用使用'cat'(https://en.wikipedia.org/wiki/Cat_(UNIX)#Useless_use_of_cat) – Biffen

+1

在Perl中,'-00'意味着使用段落模式,這意味着讀入'$ _'的每一行都以'\ n \ n +'結尾(而不是根據默認行讀取的'\ n')。這意味着他們之後永遠不會有'}'。你想要的是咕嚕輸入,即'-0777'。 – TLP

+0

補充@ TLP的評論:類似地,'awk'中的'-v RS =「」'也會激活_paragraph_模式,所以'gsub'永遠不能匹配'\ n \ n'。因此,該命令相當於'awk -v RS =''''file.tex',它有效地簡化了空行。 要真正閱讀_entire_文件,你必須使用'-v RS ='^ $''和_GNU_'awk',或者使用BSD'awk',像'-v RS = $'\ 3' '(任何_single_ char。不希望在輸入中)。 – mklement0

回答

3

你沒有提供任何樣品的輸入和預期的輸出所以這是一個猜測,但也許這是你在找什麼:

$ cat file 
a 
b 

c 

} 
d 

$ awk '/^$/{f=1;next} f{if(!/^}/)print "";f=0} 1' file 
a 
b 

c 
} 
d 
+1

++用於一種高效的解決方案,它不首先將整個文件讀入內存。 – mklement0

1

與sed的一種方式:

sed -i -n ':a;N;$!ba;s/\n\n}/\n}/g;p' file.tex 

細節:

:a    # defines the label "a" 
N    # append the next line to the pattern space 
$!ba   # if it is not the last line, go to label a 
s/\n\n}/\n}/g # replace all \n\n} with \n} 
p    # print 

i參數將更改文件。 n參數可防止自動打印行。

+1

不錯,但需要_GNU_'sed';沒有'-i'的版本也可以使用_BSD_'sed':'sed -n -e':a'-e'N; $!ba'-e's/\ n \ n}/\'$ '\ n''}/g; p'file.tex'(單獨的'-e'選項需要終止標籤名稱;替換字符串需要字面換行符)。可悲的是,在沒有後綴的情況下使用'-i'時,與_both_版本配合使用的單個命令是不可能的。帶'-i'的_BSD_'sed'命令:'sed -i''-n -e':a'-e'N; $!ba'-e's/\ n \ n}/\'$' \ n''}/g; p'file.tex'。由於OP提到大文件,所以值得明確指出的是,該解決方案將_whole_文件讀入內存。 – mklement0

+1

@ mklement0:的確,有可能有一種方法(如測試'/ \ n [^} \ n] /')來釋放內存。 –

1

Nix風格線路過濾器逐行處理文件。因此,你必須做額外的事情來處理一個表達式,其中跨越行。

正如別人所說,'\n\n'只是一個空行,並且與正則表達式/^$/匹配。也許最有效的做法是保存每一個空行,直到你知道下一行是否在行的開始處包含一個閉括號。

cat file.tex | perl -ne 'if ($b) { print $b unless m/^\}/; undef $b; } if (m/^$/) { $b=$_; } else { print; } END { print $b if $b; }' 

,並清除了這一切,我們添加一個END塊,處理該文件的最後一行是空白的情況下(我們要保持它)。

+1

只是爲了清楚,雖然行符號線逐行處理輸入,但不包括面向記錄的awk,不像sed,grep等是面向行的。然而,OP所面臨的問題顯然就是他的「記錄」(我們不知道那些沒有給出樣本輸入的內容)太大而不適合記憶。 –

+0

覆蓋尾部空行的邊緣情況,但是你的'perl'命令被破壞:'$ b'需要在'if($ b)'分支中重置,elseif'必須是一個單獨的'if '聲明:'cat file.tex | perl -ne'if($ b){print $ b unless m/^ \} /; $ B = 「」; } if(m/^ $ /){$ b = $ _; } else {print; } END {print $ b if $ b; }''。實質上,這是@ EdMorton'awk'解決方案的'perl'表親,所以它也具有不會一次啜食整個文件的優點。 Ed:沒有閱讀整個文件是我在問題中可以檢測到的唯一約束;沒有看到任何重新記錄的大小。 – mklement0

+0

@ mklement0 OP表示'awk -v RS =「」''它工作,但不適用於大文件',因此他嘗試使用小於整個文件的記錄(段落在這種情況下),它仍然是太大了。 –

1

這應該工作:

cat file.tex | sed -e 's/\\n\\n}/\\n}/g' 

如果\n\n}寫成原始字符串。

或者,如果它是新行:

cat file.tex | sed -e ':a;N;$!ba;s/\n\n}/\n}/g' 

另一種方法:

如果第一\n任何新行:

text=$(< file.tex) 
text=${text//$'\n\n}'/$'\n}'} 
printf "%s\n" "$text" #> file 

如果第一\n是一個emp TY線:

text=$(< file.tex) 
text=${text//$'\n\n\n}'/$'\n\n}'} 
printf "%s\n" "$text" #> file 
+2

根據OP自己的解決方案嘗試來判斷,假設_actual_換行符必須匹配,而不是_literal_'\ n'序列。 – mklement0

+1

感謝您的更新,但現在您已經有效地複製了@Casimir和Hippolyte的解決方案,同樣的注意事項也適用:需要_GNU_'sed',整個文件一次被讀取。 – mklement0

+0

增加了另一種解決方案,據推測可以在任何地方工作,讓我知道它是否有任何限制,@ mklement0 – Jahid

1

,你問

perl -i -0777 -pe's/\n(?=\n})//g' file.tex 
+1

++優雅,但請注意,整個文件被讀取_at once_,並由OP評論他們自己的'awk'命令來判斷,他們想要避免這一點。 – mklement0

0

如果你有機會到節點可以使用rexreplace

npm install -g regreplace 

,然後運行該Perl的命令將做

rexreplace '\n\n\}' '\n\}' myfile.txt 

如果你有更多的文件在一個目錄data你可以做

rexreplace '\n\n\}' '\n\}' data/*.txt