Bash - 读作 $@ 的后备

Bash - 读作 $@ 的后备

我有一个有效的 bash 脚本(在 OSX 上运行),它以文件和目录作为输入,并执行类似

for inputFile in $@
do
[someStuff]
done

但我想提供一个“后备”,意思是,如果脚本启动时没有参数(例如双击),它可以在那时接受输入,让用户将文件直接放在终端上(可能通过read但不是强制性的,我愿意接受更好/不同的解决方案)。

我猜我应该使用某种if语句,但我不知道该怎么做。我不想通过重复以下操作将脚本的大小翻倍[一些东西]对于每种情况。

谢谢。

答案1

if [ $# -eq 0 ]; then
    read -p "File: " altfile
    set "$altfile"
fi

for inputFile in "$@"
    ...etc

编辑:如果您想允许读入多个文件名,您可以在一行上执行此操作,并用空格分隔它们:

if [ $# -eq 0 ]; then
    read -p "File(s): " -a altfiles
    set "${altfiles[@]}"
fi

或每行一个:

if [ $# -eq 0 ]; then
    altfiles=()
    echo "Enter filenames one per line, enter Control-D after the last filename"
    while read -p "File: " altfile; do
        altfiles+=("$altfile")
    done
    set "${altfiles[@]}"
fi

请注意,这两种方法都会从文件名中删除反斜杠,这意味着您可以从 Finder 中拖动文件并且它们会被正确解析,但手动输入带有奇怪字符的文件名可能无法按您预期的方式工作。

答案2

假设您的备份计划是在标准输入上提供一个文件列表,每行一个,而您的主要计划是在命令行上提供一个文件列表,每个参数一个:

#!/bin/bash
declare -a files # let's use an array to avoid whitespace tokenization
let filecnt=0
if [ "$#" -gt 0 ]; then
    # we have args!
    for f in "$@"; do
        files[$filecnt]="$f"
        ((filecnt++))
    done
else
    # we have filenames on stdin!
    while read -e line; do
        files[$filecnt]="$line"
        ((filecnt++))
    done
fi

# now do whatever you need to do with the filenames
for filename in "${files[@]}"; do
    echo "$filename"
done

与 Unix 上的惯例一样,在其自己的行上按 Ctrl-D 会结束 stdin,并允许程序继续。

但你提到了“drop”,这让我想到你会真的比如拖放功能。为此,鸭嘴兽可能需要研究一下。我相信它会将您的脚本捆绑到一个应用程序中,该应用程序会为您创建一个拖放目标,并将拖放的文件作为参数传递给您的脚本。

更新:如果您希望文件之间只包含任何旧的空格,以便多个文件可以位于同一行,请将 while 循环替换为:

while read -ea lfiles; do
    for f in "${lfiles[@]}"; do 
        files[$filecnt]=$f
        ((filecnt++))
    done
done

或者如果您希望只有一行包含所有文件,则可以将整个 while 循环简化为:

read -ea files

但请注意,使用此方法,您必须在您实际想要作为文件或目录路径一部分的任何空格前面输入一个反斜杠,否则脚本会将您的路径名解析为多个单独的文件!(不过,从 Finder 拖放到终端将自动为您执行此反斜杠转义。)

相关内容