该脚本用于压缩并归档给定目录中任何大于 20 MB 的文件:
#!/bin/bash
#Variables
BASE=/home/sengh/scripts
DEPTH=1 #how deep to go in the find operation
RUN=0
#Check if the directory is present or not
if [ ! -d $BASE ]
then
echo "directory does not exist: $BASE"
exit 1
fi
#Create 'archive' folder if not present
if [ ! -d $BASE/archive ]
then
mkdir $BASE/archive
fi
#Find the list of files larger than 20 MB
for i in 'find $BASE -maxdepth $DEPTH -type f -size +20M'
do
if [ $RUN -eq 0 ]
then
echo "[$(date "+%Y-%m-%d %H:%M:%S")] archiving $i ==> $BASE/archive"
gzip $i || exit 1
mv $i.gz $BASE/archive || exit 1
fi
done
我得到以下输出:
[2024-01-26 23:55:06] 归档查找 $BASE -maxdepth $DEPTH -type f -size +20M ==> /home/sengh/scripts/archive gzip: 无效选项 -- 'x' 尝试 `gzip --help' 获取更多信息。
我尝试浏览 gzip 的手册页,但没有帮助,因为我没有使用“x”这样的选项。请帮忙。
答案1
使用以下方法更容易zsh
:
#! /bin/zsh --
mkdir -p -- ~/scripts/archive || exit
PROMPT4='[%D{%F %T}] ' # for the xtrace output to be prefixed by the
# current time in [%F %T] strftime format
set -o xtrace -o noclobber # xtrace provides a trace of each command
# being executed. noclobber prevents
# redirection clobbering files.
for file (~/scripts/*(ND.LM+20))
gzip -c -- $file > ~/archive/scripts/$file.gz && rm -f -- $file || exit
该(ND.LM+20)
部分是全局限定符,这进一步限定了全局扩展:
N
仅适用nullglob
于该一个 glob,以便该 glob 扩展为空,而不是在没有匹配项时返回错误。您通常希望在 for 循环列表中使用它。D
仅适用dotglob
于该一个 glob 来包含隐藏文件(就像find
默认情况下一样)LM+20
相当于 GNUfind
的-size +20M
限制扩展为大小舍入为整数兆字节且严格大于 20 的文件,即 20971521 字节L
或更长的文件。.
喜欢-type f
是限制于常规的文件仅排除任何其他类型的文件,例如符号链接、fifos、目录、设备...
使用 bash 以相同的可靠性级别执行相同的操作相当麻烦,并且需要最新版本的 GNU 实用程序。
#! /bin/bash --
mkdir -p -- ~/scripts/archive || exit
PS4='[\D{%F %T}] ' # for the xtrace output to be prefixed by the
# current time in [%F %T] strftime format
set -o noclobber # prevents redirection clobbering files.
while IFS= read -rd '' -u3 file; do
(set -o xtrace
gzip -c -- "$file" > ~/archive/scripts/"$file".gz) &&
rm -f -- "$file"
) 3<&- || exit
done 3< <(
find -H -files0-from <(printf '%s\0' ~/scripts/archive) \
-mindepth 1 -maxdepth 1 -type f -size +20M -print0 |
sort -z
)
一如既往,你需要--
将选项与非选项参数分开确保那些以 . 开头的非选项参数不会被视为选项-
。然而,对于find
,这并没有帮助,因为即使在 之后,如果它们以(或者是, , )--
开头,它们仍然被视为谓词。对于 BSD ,只需使用 代替,但 GNU不支持这一点。然而从 4.9 版本开始,它支持从文件传递文件列表。-
(
)
!
find
find -f "$dir"
find -- "$dir"
find
-f
-files0-from
关于您的代码的一些注释:
- 在 bash 中,参数扩展必须加引号,否则它们会经历 split+glob。看什么时候需要双引号?以及有关该主题的许多其他问答。 shellcheck 还可以帮助您发现这种初学者错误。
'...'
用于强引用。'find ...'
是文字字符串find ...
。但即使您用反引号 (`...`
) 替换这些单引号,这仍然是错误的方法。看为什么循环查找的输出是不好的做法?了解详情。- 最好避免做像
if condition-not-met; then do-something-that-would-make-sure-the-condition-is-met; fi
你这样的事情,因为它介绍了if [ ! -d ...]; then mkdir...
TOCTOU 比赛条件。使用 时-p
,mkdir
仅当目录(以及所有前导目录组件)不存在时才创建该目录,并且仅当目录返回后不存在时才会失败。
在这里,第一点和第二点的组合导致gzip
返回该错误。
该循环正在循环遍历一个值,即find $BASE -maxdepth...
。由于您忘记引用$i
,gzip $i
受到$i
split+glob 的影响,并且默认值$IFS
,gzip
被传递find
, $BASE
,-maxdepth...
的内容中的单独参数$i
作为一个参数。
GNU 实用程序有一个错误特征,即使在非选项参数之后,选项仍然可以被识别(这使得不要忘记那些分隔符变得更加重要--
),因此-maxdepth
被视为 的选项gzip
,并且被视为组合在一起的单字母选项,因此相同作为-m
-a
-x
-d
...
-m
是一个当前未记录的选项,告诉gzip
/gunzip
不保留修改时间,-a
,在类 Unix 系统上也未记录,缩写--ascii
仅与 Microsoft Windows 相关(并且其名称相当具有误导性),-x
不在受支持的选项之列,甚至没有记录的,所以你会得到这个错误。