簡體   English   中英

sed/regex 模式來搜索和替換文件名中的數字

[英]sed/regex pattern to search and replace numbers in a filename

我有一組 3 個文件,用日期編碼:

abc1_bbb_yyy_2_8_15.csv
abd1_bba_yzy_11_8_16.csv
aby1_qba_yay_11_21_16.csv

最后三個數字代表日期:

2815
11816
112116

我需要使用單個正則表達式過濾器僅提取與文件名中的日期相對應的數字,該過濾器還將結果轉換為 MMDDYY 格式:

020815
110816
112116

謝謝你的幫助!

awk -F'[_.]' '{printf "%02d%02d%02d\n",$(NF-3),$(NF-2),$(NF-1)}'

這似乎是一個有趣的問題,可以嘗試用 sed 解決。

我更喜歡 TessellatingHeckler 的 perl 方法。 :-)

編輯:睡在上面后,我更喜歡 jthill 的 awk 方法。
嘗試用 sed 解決問題在技術上很有趣,但不是我想要長期生活的東西。

數據文件

示例數據文件...

$ cat foo.dat
abc1_bbb_yyy_2_8_15.csv
abd1_bba_yzy_11_8_16.csv
aby1_qba_yay_11_21_16.csv
$

示例結果

請注意, sed -r 啟用常規 epxression 擴展。

$ sed -rf foo.sed < foo.dat
020815
110816
112116
$ 

吃飽了

通常我不會這么冗長。 :-)

但我認為這些評論會使目的更加明確。

# Put a wedge between "prefix" and "date.CSV" part.
# We don't salvage the .csv extension, that drops off here.
# Note the space padding before/after \1, we'll use that shortly.
s/([0-9_]+)\.csv/ \1 /g
#    in:  "abc1_bbb_yyy_2_8_15.csv"
#    out: "abc1_bbb_yyy _2_8_15 "
# (If I knew how to do non-greedy matching in sed we could
# strip the prefix e.g. "abc1_bb_yyy" part here as well,
# but if we try that we end up with just "_15 ", e.g. our
# other month & day get eaten).
# Hence sacrificial space character that our
# next substitution will use to cut the prefix.

# Cut the prefix.
# strip up to, but not including, the first non-space char.
# (I don't think you can do non-greedy matching in sed).

s/^.* ([^ ])/\1/
#    in:  "abc1_bbb_yyy _2_8_15 "
#    out:              "_2_8_15 "

# change our underscores to two space chars.
# (turns out we need two intermediate spaces for
# the next substitution to work as a single "global" substitution)
s/_/  /g
#    in:   "_2_8_15 "
#    out:  "  2  8  15 "
# At this point all of our month/day/year parts 
# have *two* spaces between them.

# Next we do zero-padding if necessary.
s/ ([0-9]) / 0\1 /g
# Important: we're looking for a single space before
# and after any single digit.
#    in:  "  2  8  15 "
#   out:  " 02 08  15 "
# input broken out by single chars with "spc"= 1 space char.
#         +---+---+---+---+---+---+---+---+---+---+---+
# input:  |spc|spc| 2 |spc|spc| 8 |spc|spc| 1 | 5 |spc|
#         +---+---+---+---+---+---+---+---+---+---+---+
#              \         / \         /     no match, not
#               \       /   \       /      a single digit.
#                \     /     \     /
#                match 1     match 2
#               /       \   /       \
#              /         \ /         \
#         +---+---+---+---+---+---+---+---+---+---+---+
# result  |spc| 0 | 2 |spc| 0 | 8 |spc|spc| 1 | 5 |spc|
#         +---+---+---+---+---+---+---+---+---+---+---+
# Without "two spaces" between digits this
# would require 3 separate substitutions...
# doing a single global e.g. s/ ([0-9]) / 0\1 /

# Pretty much done, just strip the spaces.
s/ //g
#   in:   " 02 08  15 "
#   out:  "020815"

正如其他人指出的那樣, sed不是這項工作的最優雅的工具。 使用 perl,

fn='abc1_bbb_yyy_2_8_15.csv abd1_bba_yzy_11_8_16.csv aby1_qba_yay_11_21_16.csv'
for x in $fn; do
  echo $x | perl -n -e 'printf("%02d%02d%02d\n",/(\d+)_(\d+)_(\d+)\./)'
done

如果你真的被限制使用sed ,那么這里有一個方法。 第一個正則表達式在下划線前面的數字前面加上一個零。 第二個查找后跟下划線或點的數字字符串,並刪除每次出現的最后兩位數字以外的所有數字。 最后提取一個由 6 位數字組成的最終字符串,前面是任何內容,但后面是非數字。

for x in $fn; do
  echo $x | sed -e 's/_\([0-9]\)/_0\1/g' \
    -e 's/[0-9]*\([0-9]\{2\}\)[_.]/\1/g' \
    -e 's/.*\([0-9]\{6\}\)[^0-9]*$/\1/'
done

結果:

$ for x in $fn; do
>       echo $x | sed -e 's/_\([0-9]\)/_0\1/g' \
>         -e 's/[0-9]*\([0-9]\{2\}\)[_.]/\1/g' \
>         -e 's/.*\([0-9]\{6\}\)[^0-9]*$/\1/'
>     done
020815
110816
112116

嘗試這個:

REST=cat # 管道的其余部分是什么...

( cat <<EOF
abc1_bbb_yyy_2_8_15.csv
abd1_bba_yzy_11_8_16.csv
aby1_qba_yay_11_21_16.csv
EOF
)\
| cut -d_ -f4-6 \
| cut -d. -f1 \
| sed -e 's/\([0-9][0-9]*\)/0\1/g' \
    -e 's/0\([0-9][0-9]\)/\1/g' \
    -e 's/_//g' \
| $REST

將文件名放入 t.txt

abc1_bbb_yyy_2_8_15.csv
abd1_bba_yzy_11_8_16.csv
aby1_qba_yay_11_21_16.csv

然后

$ cat t.txt | perl -p -e 's/(?<=_)(\d)(?=_)/0\1/g' | perl -p -e 's/.*(\d\d)_(\d\d)_(\d\d)\.csv/\1\2\3/'
020815
110816
112116

這不完全是 sed/awk/grep,因為 sed 不能進行環視,而且我現在不想 AWK,但它是正則表達式和 *nixy。

[編輯:好的,不喜歡 Perl 的反對者,我的方法是先用 0 前綴個位數,然后提取兩位數對。 sed 在沒有環視或非捕獲組的情況下很難做到這一點,但這里有一個 sed 答案,使用@jgreve 的想法首先插入一個楔子。 這也包括 YYYYMMDD 格式的輸出,假設所有年份都是 20:

#                  #wedge        #single n to 0n            #extract __dd__mm__yy                                   to 20yymmdd
cat t.txt | sed -e 's/_/__/g' -e 's/_\([0-9]\)_/_0\1_/g' -e 's/.*__\([0-9][0-9]\)__\([0-9][0-9]\)__\([0-9][0-9]\)\.csv/20\3\2\1/'

]

暫無
暫無

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

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