Передача подстановочного знака в scp в сценарии оболочки bash

Я пишу оболочку Bash для scp'ing в и из определенного хоста с определенным именем пользователя, например:

johny@bonjour:~/bin$ cat scpphcl 
#!/bin/bash

download=false
upload=false
local=""
remote=""

usage()
{
    echo "Usage: $0 -d[-u] -l <LocalPath> -r <RemotePath>"
    exit 1
}

while getopts "h?dul:r:" opt; do
    case "$opt" in
    h|\?)
        usage
        ;;
    d)
        download=true
        upload=false
        ;;
    u)
        download=false
        upload=true
        ;;
    l)
        local=$OPTARG
        ;;
    r)
        remote=$OPTARG
        ;;
    esac
done

if [[ -z $local || -z $remote ]]; then
    echo "Need to provide local and remote path."
    usage
fi

if $download; then
    scp somebody@somehost:"$remote" $local
elif $upload; then
    scp $local somebody@somehost:"$remote"
else
    echo "Neither download nor upload?"
    exit 1
fi

if [[ $? -ne 0 ]]; then
    echo "Something wrong happened in the scp process."
    exit 1
fi

exit 0

Он хорошо работает с обычными именами файлов, но если в поле локального имени файла есть какой-либо подстановочный знак, он не будет работать правильно.

johny@bonjour:~/test$ scpphcl -u -l * -r /u/somebody/temp
Need to provide local and remote path.
Usage: /Users/johny/bin/scpphcl -d[-u] -l <LocalPath> -r <RemotePath>

Существует обходной путь, использующий кавычки sinqle вокруг аргумента локального файла, если в нем есть подстановочный знак:

johny@bonjour:~/test$ scpphcl -u -l '*' -r /u/somebody/temp

Но даже этот обход не будет работать, если команда введена вне папки test:

johny@bonjour:~/test$ cd ..
johny@bonjour:~$ scpphcl -u -l 'test/*' -r /u/somebody/temp

Это не работает и будет зависать в процессе scp.

Любая помощь в том, как передать подстановочный знак в локальных именах файлов с помощью оболочки Bash?

1 ответ

Решение

Вероятно, лучше не требовать от пользователей цитирования шаблонов подстановочных знаков. Вместо этого я бы изменил интерфейс вашей программы так, чтобы он принимал любое количество локальных путей после аргументов option:

echo "Usage: $0 [-d|-u] [-r <RemotePath>] <LocalPath>..."

При чтении опций используйте их shift:

while getopts "h?dur:" opt; do
    case "$opt" in
    h|\?)
        usage
        exit 0
        ;;
    d)
        download=true
        upload=false
        ;;
    u)
        download=false
        upload=true
        ;;
    r)
        remote="$OPTARG"
        ;;
    *)
        usage >&2
        exit 1
        ;;
    esac
done
shift $((OPTIND-1))

Теперь остальные позиционные аргументы являются локальными именами файлов (и могут быть доступны с "$@" - обратите внимание на все важные двойные кавычки):

if test -z "$*"  # no LocalPath arguments!
then usage >&2; exit 1
elif $download
then exec scp somebody@somehost:"$remote" "$@"
elif $upload
then exec scp "$@" somebody@somehost:"$remote"
fi
Другие вопросы по тегам