SHELL:输出不符合预期

SHELL:输出不符合预期

我正在做下面的作业,我对我的程序有一个疑问:

编写一个复制文件的 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手册是一个很好的起点。

相关内容