我正在尝试将文件从一个位置复制到另一个位置,下面给出一些示例:
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
从当前目录开始查找文件。默认情况下,GNU
find
使用 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 将按原样保留,并且您将收到一条错误消息。