BASH Shell 使用通配符查找多个文件并使用操作执行循环

问题描述 投票:0回答:1

我有一个使用应用程序调用的脚本,但无法从命令行运行它。我导出调用脚本的目录,并在下一个变量中向上提升 1 级,用于存储我的文件。从那里我有 3 个带有完整路径和文件名(带通配符)的变量,我将其称为“掩码”。

我需要找到每个掩码并对其“执行某些操作”(将其名称复制/写入新文件,无论其他)。做某事部分不是我的障碍,因为当我使用单个掩码时,我已经做得很好,但我想在单个循环中干净地完成它,而不是重复循环,并且如果可能的话,只单独引用每个掩码.

假设在下面的 $FILESFOLDER 目录中,我有 2 个现有文件,aaa0.csv 和 bbb0.csv,但没有与 ccc*.csv 掩码匹配的文件。

#!/bin/bash
SCRIPTFOLDER=${0%/*}
FILESFOLDER="$(dirname "$SCRIPTFOLDER")"
ARCHIVEFOLDER="$FILESFOLDER"/archive
LOGFILE="$SCRIPTFOLDER"/log.txt

FILES1="$FILESFOLDER"/"aaa*.csv"
FILES2="$FILESFOLDER"/"bbb*.csv"
FILES3="$FILESFOLDER"/"ccc*.csv"

ALLFILES="$FILES1
$FILES2
$FILES3"

#here as an example I would like to do a loop through $ALLFILES and copy anything that matches to $ARCHIVEFOLDER.
for f in $ALLFILES; do
  cp -v "$f" "$ARCHIVEFOLDER" > "$LOGFILE"
done

echo "$ALLFILES" >> "$LOGFILE"

真正让我头晕的事情是当我运行这样的东西时(我没有使用复制命令来完成它),日志文件最后显示:

文件文件夹/aaa0.csv 文件文件夹/bbb0.csv 文件文件夹/ccc*.csv

我期望回显 $ALLFILES 只是为了向我展示掩码

文件文件夹/aaa*.csv 文件文件夹/bbb*.csv 文件文件夹/ccc*.csv

在我的“做某事”区域中,如果可能的话,我需要能够使用任何方法通过带通配符的完整路径/名称来查找文件。有时我的网络会因维护而关闭,我不想冒更改目录失败的风险。我很少在 Linux 上工作(主要是 SQL 背景),所以请随意指出我做错的所有事情。预先感谢!

bash loops wildcard
1个回答
1
投票

这是一个轻度重构,干扰变量明显减少。

#!/bin/bash
script=${0%/*}
folder="$(dirname "$script")"
archive="$folder"/archive
log="$folder"/log.txt  # you would certainly want this in the folder, not $script/log.txt

shopt -s nullglob
all=()
for prefix in aaa bbb ccc; do
    cp -v "$folder/$prefix"*.csv "$archive"
    all+=("$folder/$prefix"*.csv)
done >>"$log" # append, don't overwrite
echo "${all[@]}" >> "$log"

循环中附加输出或

cp -v
而不是覆盖的更改是一个错误修复;否则日志将仅包含最后一次循环迭代的输出。

done
之后重定向的更改是一个小优化;通过此更改,shell 无需在每次在循环内运行
cp
时重新打开文件并查找文件末尾。

我可能更喜欢让文件从循环内部回显,每行一个,而不是将它们全部收集在一个巨大的行上。然后你可以删除数组

all
,而是简单地

    printf '%s\n' "$folder/$prefix"*.csv >>"$log"

shopt -s nullglob
是一个 Bash 扩展(因此不能与
sh
一起使用),它表示丢弃与任何文件不匹配的任何通配符(默认行为是如果 glob 不匹配任何内容,则不展开它们)。如果您想要不同的解决方案,也许请参阅测试 glob 在 Bash 中是否有任何匹配项

你应该使用小写字母表示你的私有变量所以我也改变了它。还要注意

script
变量实际上并不包含文件夹名称(或者我们成年人更喜欢称之为“目录”);修复在您的尝试中发现的错误。

如果您的通配符更复杂,您可能需要为每个模式创建一个数组。

tmpspaces=(/tmp/*\ *)
homequest=($HOME/*\?*)
for file in "${tmpspaces[@]}" "${homequest[@]}"; do
    : stuff with "$file", with proper quoting
done

处理可能包含 shell 元字符的文件名的唯一可靠方法是使用数组变量;使用字符串变量作为文件名是出了名的脆弱。 也许另见https://mywiki.wooledge.org/BashFAQ/020

© www.soinside.com 2019 - 2024. All rights reserved.