У меня есть рабочий скрипт bash (работающий на OSX), который принимает файлы и каталоги в качестве входных данных и делает что-то вроде

for inputFile in $@
do
[someStuff]
done

но я хочу предоставить «запасной вариант», означающий, что если скрипт запускается без аргументов (например, двойной щелчок), он может принимать данные в это время, позволяя пользователю удалять файлы непосредственно на терминале (возможно, через read но не обязательно, я открыт для лучшего / разных решений).

Я предполагаю, что я должен использовать какое-то утверждение if , но я не уверен, как. Я бы не хотел дублировать размер скрипта в два раза, повторяя [someStuff] для каждого случая.

Спасибо.

2 ответа2

2
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, и они будут правильно анализироваться, но ввод имен файлов с забавными символами вручную может не работать, как вы ожидаете.

1

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

#!/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 в отдельной строке завершает стандартный ввод и позволяет программе продолжаться.

Но вы упомянули "отбрасывание", и это заставляет меня думать, что вам действительно нужна возможность перетаскивания. Для этого Утконос может быть чем-то, чтобы изучить. Я верю, что он объединит ваш сценарий в приложение, которое сделает для вас цель перетаскивания и передаст удаленные файлы в качестве аргументов вашему сценарию.

Обновление: если вы хотите, чтобы файлы содержали только один старый пробел между ними, чтобы в одной строке могли находиться несколько файлов, замените цикл while на:

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

Или, если вы ожидаете только одну строку со всеми файлами на нем, вы можете упростить весь цикл while:

read -ea files

Однако имейте в виду, что при таком подходе вы должны вводить обратную косую черту перед любым пробелом, который вы на самом деле хотите включить в путь к файлу или каталогу, или сценарий будет анализировать ваш путь как несколько отдельных файлов! (Перетаскивание из Finder в Терминал автоматически выполнит эту обратную косую черту за вас.)

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .