Bash - читать как запасной вариант к $ @

0

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

for inputFile in $@
do
[someStuff]
done

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

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

Спасибо.

user137369
источник

Ответы:

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

Gordon Davisson
источник
Разве это решение не позволило бы мне работать только с одним файлом за раз, с точки зрения read вариант? Важно иметь возможность читать несколько файлов / каталогов, иначе это будет регрессия.
user137369
Кажется, это работает точно так, как задумано (пример из одной строки), теперь я просто борюсь с частью «else». Как в добавление к этому if который использует readчто я должен положить в else часть, которая использует аргументы, переданные через командную строку при вызове скрипта, чтобы сказать «установить каждый из аргументов $ @ в $ {altfiles [@]}»?
user137369
Вам не нужна другая часть. Просто используйте for inputFile in "$@" и он будет использовать либо исходные аргументы, либо те, которые будут прочитаны позже (и будут установлены с помощью `set) в зависимости от ситуации.
Gordon Davisson
Несколько строк, и, кажется, отлично работает. Спасибо.
user137369
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 в отдельной строке завершает стандартный ввод и позволяет программе продолжаться.

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

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

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

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

read -ea files

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

Owen S.
источник
read часть не работает для меня, она просто позволяет мне добавлять файлы бесконечно, ничего не делая. Кроме того, это заставит пользователя добавить файл и создать новую строку, или они могут добавить все файлы в одну строку (разделенные пробелами)?
user137369
Я знаю и утконос, и дропскрипт, и они в какой-то степени работают нормально, но ранее в сценарии я спрашивал у пользователей номер (используя read ) и что эти приложения не могут справиться. Если только я не использую oascript для запроса этого числа, я предпочел бы сначала создать полноценный скрипт только для терминала.
user137369
Для этого решения способ сообщить об окончании стандартного ввода (чтобы программа продолжила работу) - это нажать Ctrl-D в отдельной строке. Это довольно распространенный юниксизм - вам может быть знаком этот способ закрытия стандартного ввода, например, если вы работали с интерпретаторами Python и Ruby. Это не совсем интуитивно понятно, но приятно то, что он отлично работает с передачей содержимого файла в качестве вашего стандартного ввода: myscript < filenames.txt,
Owen S.
Это конкретное решение вызывает одно имя файла в строке. Я также добавлю линейное решение, но в Mac OS X это опасно.
Owen S.
Я знаю, я рассчитываю на то, что этот терминал будет выполнять свою работу, но я все же буду делать некоторые побеги внутри сценария позже, так что я думаю, что я это понял. Спасибо, я думаю, что это работает, но я просто хотел бы знать, возможно ли не использовать Ctrl-D (т.е. установить все файлы в одну строку, нажать клавишу возврата, и она выполнится). Если это возможно, это было бы так, как я хотел.
user137369