如何避免 sed 中的多余替换?

如何避免 sed 中的多余替换?

第一个文件包含:

#. This is the file name to process: waveheight.txt
#. This is the latest data to process if exists: waveheightNew.txt
 FilNam=Project2128/Input/waveheightNew.txt
 if [[ ! -f ${FilNam} ]]; then FilNam=Project2128/Input/waveheight.txt; fi

第二个文件包含:

#. This is the file name to process: waveheightBin.txt
#. This is the latest data to process if exists: waveheightNewBin.txt
 FilNam=Project2128/Input/waveheightNewBin.txt
 if [[ ! -f ${FilNam} ]]; then FilNam=Project2128/Input/waveheightBin.txt; fi

.txt现在我需要通过更改为Bin.txt?来处理文件。使用sed "s/.txt/Bin.txt/"将导致BinBin.txt第二个文件。到sed "s/Bin.txt/.txt/"那时就sed "s/.txt/Bin.txt/"显得尴尬了。

跳过不需要的匹配会更明智吗?

答案1

您可以Bin在文本中包含要替换的内容(如果存在),这会导致它被自身替换:

sed 's/\(Bin\)\{0,1\}\.txt/Bin.txt/g'

或者如果您sed支持 ERE -E(或者-r对于某些旧版本的 GNU 或 busybox sed):

sed -E 's/(Bin)?\.txt/Bin.txt/g'

当心.是一个匹配任何单个字符的正则表达式运算符。您需要\.匹配文字

答案2

您可以使用 perl 负向后查找来匹配.txt,但只有它不是Bin.txt

perl -pe 's/(?<!Bin)\.txt/Bin.txt/g'

因此,要测试:

$ echo 'Bin.txt foo.txt' | perl -pe 's/(?<!Bin)\.txt/Bin.txt/g'
Bin.txt fooBin.txt

不幸的是,sed不提供这种构造。

答案3

您可以使用 进行条件替换sed,例如,您可以测试该行是否已包含Bin.txt,如果不包含则仅执行替换。

sed '/Bin\.txt/!s/\.txt/Bin.txt/'

这假设每行只需要一次替换。

您也可以无条件地进行替换,然后在出现错误时进行更正,正如您在问题中暗示的那样,但在同一调用中sed

sed -e 's/\.txt/Bin.txt/' -e 's/BinBin/Bin/'

答案4

您可以按GNU-sed如下所示执行此操作:

echo "$Filnam" |\
sed -e '
   s/\.txt/\n&/;T   # try to place a newline marker to the left of .txt, quit if unsuccessful
   s/Bin\n/Bin/;t   # If the marker turned out to be just to the right of Bin => Bin.txt already 
                    # existed in the name, so we needn"t do anything n take away the marker n quit
   s/\n/Bin/        # Bin could not be found adjacent to .txt so put it n take away the marker as well
'

### Below is the POSIX sed code for accomplishing the same:
sed -e '
    s/\.txt/\
&/;/\n/!b
    s/Bin\n/Bin/;/\n/!b
    s/\n/Bin/
'

相关内容