我正在做下面的作业,我对我的程序有一个疑问:
编写一个复制文件的 shell 脚本,如下所述。后缀应作为命令行参数传输。对于当前目录中指定后缀适合的每个文件,应询问用户是否应保存该文件。如果答案是肯定的,则应将该文件复制到备份目录中。如果该目录不存在,则应创建该目录。
这就是我所做的:
#!/bin/sh
usage() {
echo "Usage : prog.sh <POSTFIX> " >&2
exit 1
}
if [ $# -ne 1 ]; then
usage
fi
list=$(ls)
for name in $list; do
case $name in
*.$@)
printf "Do you want to save the file ?\n"
read answer
case $answer in
Yes|yes|y)
ls backup 2>/dev/null || mkdir backup
mv $name backup
;;
No|no|n);;
esac ;;
esac
done
假设我有以下这些文件夹:(将移动到备份目录)
bar.txt foobar.txt foo.txt mbar.txt
这是我的输出:
Do you want to save the file ?
y
Do you want to save the file ?
y
bar.txt // Why is it printing the file during the output ?
Do you want to save the file ?
y
bar.txt foobar.txt // ??
Do you want to save the file ?
y
bar.txt foobar.txt foo.txt // ??
我知道我可以这样写(而不是 ls backup 2>/dev/null || mkdir backup
)
if [ ! -d backup ] ; then
mkdir backup
fi
但我想知道为什么我的程序打印在输出期间将移动到备份的文件?
答案1
因为您ls backup 2>/dev/null
对移动到目录的每个文件都执行了操作backup
,所以接下来ls
将显示它们;
顺便说一句,您的整个脚本可以用下面的单个命令替换rsync
:
rsync --include='*.{txt,csv,xyz}' /path/to/source/* /path/to/backup/
看man rsync
了解详细信息以及如何在同步后删除。
答案2
这是因为这一行:
ls backup 2>/dev/null
您只重定向 stderr 而不是 stdout。你应该做这个:
ls backup >/dev/null 2>&1 || mkdir backup
要不就:
mkdir -p backup
您还应该注意 shellcheck 发出的所有警告:
$ shellcheck ./p.sh
In ./p.sh line 18:
read answer
^--^ SC2162: read without -r will mangle backslashes.
In ./p.sh line 24:
mv $name backup
^---^ SC2086: Double quote to prevent globbing and word splitting.
Did you mean:
mv "$name" backup
For more information:
https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...
https://www.shellcheck.net/wiki/SC2162 -- read without -r will mangle backs...
答案3
如果允许 bash,我会这样做:
#!/usr/bin/env bash
if (( $# == 0 )); then
echo "usage: ${0##*/} extension ..." >&2
exit 1
fi
dir=./backup
mkdir -p "$dir"
# if the script is called like `script.sh foo bar baz`
# then $pattern is `@(*foo|*bar|*baz)`
pattern=$(
set -- "${@/#/*}"
IFS='|'
echo "@($*)"
)
shopt -s nocasematch
for file in *; do
# right-hand side is specifically unquoted
if [[ "$file" == $pattern ]]; then
read -p "Move file '$file'? [y/N] " ans
if [[ "$ans" == Y* ]]; then
mv -i -v "$file" "$dir"
fi
fi
done
由于这是一项作业,因此在提出具体问题之前,请先对您不理解的部分进行一些研究。这bash手册是一个很好的起点。