复制具有特定模式的文件

复制具有特定模式的文件

我正在尝试将文件从一个位置复制到另一个位置,下面给出一些示例:

aaa_bbb_ccc_ddd_cost_code_20140330.gz
aaa_bbb_ccc_ddd_revenue_zone_20140329.gz
aaa_bbb_ccc_ddd_benefit_extract_20140330.csv.gz
aaa_bbb_ccc_ddd_profit_zone_20150509.csv.gz
aaa_bbb_ccc_ddd_loss_zone_20140330.csv
aaa_bbb_ccc_ddd_username.csv.gz

从上面的列表中,应复制的文件应采用以下格式:

aaa_bbb_ccc_ddd_cost[or]revenue[or]benefit[or]profit[or]loss_yyyymmdd.csv.gz

这意味着文件

aaa_bbb_ccc_ddd_loss_zone_20140330.csv
aaa_bbb_ccc_ddd_username.csv.gz

不应该被复制。

另外,我需要将它分配给一个变量,我正在尝试类似的方法,但它似乎不起作用:

FILENAME="egrep 'aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit)_code_[0-9]{8}.csv.gz'"

我尝试将其分配给变量的原因是因为我需要稍后在代码中使用它来执行以下操作:

SOURCE_DIR="/temp"
DESTN_DIR="/output"
FILENAME=`egrep 'aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit)_code_[0-9]{8}.csv.gz'`
echo "FILENAME is:" $FILENAME
for SAMPLE_FILE in $(ls "$SOURCE_DIR/$FILENAME")
do
cp $SAMPLE_FILE $DESTN_DIR
done

有没有其他方法可以实现这一目标?

答案1

使用find及其-exec选项(这里使用 GNUfind作为其-regex谓词):

find . -regextype posix-egrep -regex '.*/aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit|loss)_[[:alpha:]]+_[0-9]+\.csv\.gz' -exec mv {} "$DESTN_DIR" \;

笔记:

  • find .告诉find从当前目录开始查找文件。

  • 默认情况下,GNUfind使用 emacs 风格的正则表达式。我更喜欢-regextype posix-egrep,但您可以切换到您熟悉的任何受支持的样式。

  • 使用正则表达式来选择文件:-regex '.*/aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit|loss)_[[:alpha:]]+_[0-9]+\.csv\.gz'.这允许标准前缀aaa_bbb_ccc_ddd_后跟由 指定的单词之一(cost|revenue|benefit|profit|loss),后跟另一个未指定的单词 ,_[[:alpha:]]+后跟日期 ,_[0-9]+最后跟所需的扩展名.csv.gz。您可能需要对此进行微调。

  • 找到的任何此类文件都将通过 移动到目标目录-exec mv {} "$DESTN_DIR" \;。当find找到匹配的文件时,它会执行此命令并替换{}文件名。即使文件名包含空格、换行符或其他难读的字符,这也将起作用。

使用正则表达式的默认 (emacs) 样式

GNU 正则表达式的默认样式find需要对分组和交替运算符进行一些转义:

find . -regex '.*/aaa_bbb_ccc_ddd_\(cost\|revenue\|benefit\|profit\|loss\)_[[:alpha:]]+_[0-9]+\.csv\.gz' -exec echo mv {} targetdir \;

苹果电脑

Mac OSX 版本find(手册页在这里) 支持-regex但不支持-regextype。不过,如果它的正则表达式语法需要一些细微的改变,我不会感到惊讶。

IBM AIX 5

IBM AIX 版本的手册页find这里。确实如此不是支持-regex

答案2

zsh

setopt extendedglob
source_dir="/temp"
destn_dir="/output"
pattern='aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit)_code_[0-9](#c8).csv.gz'
print -r "pattern is: $pattern"
cp -- $source_dir/$~pattern $destn_dir

ksh93 模式可以用以下方式表达:

aaa_bbb_ccc_ddd_@(cost|revenue|benefit|profit)_code_{8}(\d).csv.gz

ksh88

aaa_bbb_ccc_ddd_@(cost|revenue|benefit|profit)_code_[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].csv.gz

但是,那扩展通配符不能在变量内部工作,以避免破坏 POSIX 兼容性:

echo @(a)

根据 POSIX,这是错误的语法,因此ksh可以将其设为新的 glob 运算符。然而:

x='@(a)'
echo $x

完全按照 POSIX 指定,并且意味着输出@(a)(使用 IFS 的默认值),而不是当前目录中a调用的文件。a

所以你需要求助于使用eval,这可能很难做到正确

pattern='aaa_bbb_ccc_ddd_@(cost|revenue|benefit|profit)_code_{8}([0-9]).csv.gz'
print -r "pattern is: $pattern"
eval 'cp -- "$source_dir"/'"$pattern"' "$destn_dir"'

答案3

这可以用一行完成:

find /temp -maxdepth 1 -type f | \
 grep -P 'aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit)_.*[0-9]{8}' | \
 xargs cp -t /output
  • find列出没有子文件夹的文件夹的内容。
  • grep你的文件名
  • 并将cp它们复制到目标目录 ( -t)

你只需要调整你的正则表达式,因为你的问题并不是 100% 清楚。有的文件有.csv.gz,有的有.csv,有的有.gz

答案4

任何现代 shell 都直接支持您想要的内容,不是使用基本的 glob 语法,而是像这样:

cp aaa_bbb_ccc_ddd_{cost,revenue,benefit,profit,loss}_[0-9]*.csv.gz destination_dir

这会扩展为五个参数,每个参数都是以下形式的 glob..._keyword_<digits>...

因此,为了回答你的第二个问题,以下是如何将每个变量依次分配给一个变量:

for FNAME in aaa_bbb_ccc_ddd_{cost,revenue,benefit,profit,loss}_[0-9]*.csv.gz
do
    echo $FNAME
    if [ -e $FNAME ]
    then
        cp $FNAME <destination>
    fi
done

存在性检查 ( if [ -e $FNAME ]) 是因为如果五个 glob 中的任何一个不匹配任何内容,则该 glob 将按原样保留,并且您将收到一条错误消息。

相关内容