Передайте результат команды, если она успешна

14
INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml | head -1 | xargs basename`

Я хотел выполнить вторую команду ( head -1), только если первая команда успешна. Как мне улучшить эту команду?

Говинд Кайлас
источник
Что вы подразумеваете под успешным? lsне подведет.
Маттео
2
Но шар может потерпеть неудачу, если нет подходящих файлов.
tripleee

Ответы:

8

Попробуй это:

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml | head -1 | xargs -r basename`

Переходя xargsк -rфлаг заставит его работать только , basenameесли считывает по меньшей мере один элемент из стандартного ввода ( head -1).

head -1 будет работать, но вы не будете видеть или захватывать какие-либо выходные данные из него.

Кроме того, если вы не хотите, чтобы пользователь видел какие-либо сообщения об ошибках ls, вы можете перенаправить lsпоток stderr в /dev/null.

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename`

Также обратите внимание, что я добавил кавычки вокруг $MY_DIR. Таким образом, команда не потерпит неудачу, если $MY_DIRсодержит пробелы.

Если вы используете современную оболочку, такую ​​как bash, вы должны использовать $( )оболочку захвата вместо обратных галочек. Вам также следует подумать об изменении стиля ваших переменных. Как правило, следует избегать использования имен переменных в верхнем регистре в сценариях. Этот стиль обычно зарезервирован для зарезервированных и переменных среды.

input_file=$(ls -rt "$my_dir"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename)

источник
Разве это не b0rk, если нет файлов?
Мел Бойс
На самом деле: ls -rt GLOB 2>/dev/null | head -n1 | xargs -r basenameработает.
Мел Бойс
@MelBoyce: я добавил это к своему ответу. Благодарю.
ls - инструмент для интерактивного просмотра информации о файлах. Его вывод отформатирован для людей и будет вызывать ошибки в скриптах. Используйте шары или найдите вместо этого. Понять почему: mywiki.wooledge.org/ParsingLs
cinelli
@cinelli: это правда. Я просто отвечал на вопрос.
9

В конвейере все команды запускаются и выполняются одновременно, а не одна за другой. Так что вам нужно где-то хранить выходные данные.

if ls_output=$(ls -rtd -- "$MY_DIR"/FILE.*.xml); then
  first_file=$(printf '%s\n' "$ls_output" | head -n 1)
  first_file_name=$(basename -- "$first_file")
fi

Обратите внимание, что предполагается, что имена файлов не содержат символов новой строки. Использование xargsтакже означало бы проблемы с пустыми символами, одинарными и двойными кавычками и обратной косой чертой. Если оставить переменные без кавычек, это будет означать проблемы с пробелами, символами табуляции и подстановочными символами. Забывание --означало бы проблемы с именами файлов, начинающимися с -.

Чтобы получить базовое имя самого старого файла zshбез каких-либо ограничений на символы (также устраняется проблема ограниченного размера аргументов команды):

 first_file_name=($MY_DIR/FILE.*.xml(Om[1]:t))

Если совпадений нет, эта команда завершится ошибкой и прервет сценарий. Вы также можете сделать:

 first_file_name=($MY_DIR/FILE.*.xml(NOm[1]:t))

В этом случае first_file_nameмассив будет содержать 1 элемент, если есть совпадение, или 0, если нет. Затем вы можете сделать:

 if (($#first_file_name)); then
    printf 'Match: %s\n' $first_file_name
 else
    echo >&2 No match
 fi
Стефан Шазелас
источник
4

Найти последний измененный файл в каталоге:

latest() {
  local file path=${1:-.} ext=${2-} latest
  for file in "${path%/}"/*"$ext"; do 
    [[ $file -nt $latest ]] && latest=$file
  done
  [[ $latest ]] && printf '%s\n' "$latest"
}

Использование: latest [directory/path/ [.extension]]

Вместо вызова basename, используйте расширение параметра.

in_file=$(latest in/my/dir .xml)
base_fn=${in_file##*/} base_fn=${base_fn%.*}

В каталоге с таким содержанием:

foo.xml bar.xml baz.xml newest.xml

Содержимое переменной base_fn будет: newest

Чтобы использовать это правильно, чтобы удовлетворить цели вашего запроса:

check_dir=/path/to/check
check_ext=.xml
if in_file=$(latest "$check_dir" "$check_ext"); then
  base_fn=${in_file##*/} base_fn=${base_fn%.*}
else
  printf '%s\n' "No file found in $check_dir" >&2
fi

РЕДАКТИРОВАТЬ: после рассмотрения вопроса, я понял, что рассматриваемая lsкоманда ищет самый старый файл в каталоге. эту же функцию можно было бы переименовать oldestи [[ $file -ot $oldest ]] && oldest=$fileвместо этого добиться того же эффекта. извиняюсь за любую путаницу.

важно отметить, что вы абсолютно ни при каких обстоятельствах в человечестве не должны анализировать вывод ls. никогда.

Джош МакГи
источник
Обратите внимание, что в отличие от lsподходов на основе, если есть символические ссылки, будет учитываться время модификации цели символических ссылок (добавьте -Lпараметр, lsчтобы получить то же поведение там).
Стефан Шазелас
0

Я думаю, что лучший вариант здесь:

ls -rf $MY_DIR/FILE.*.xml
if [ $? -eq 0 ]; then
      INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml|head -1|xargs basename `
else
      echo "Error!"
fi

если файла нет, то код возврата ls равен 2, но если он найдет какой-то файл, то будет 0.

BitsOfNix
источник
2
Вместо сравнения $?Against 0, вы можете сделать это: if ls -rf $MY_DIR/FILE.*.xml ; then.
Кроме того, вы можете перенаправить вывод первой команды на /dev/null. ls -rf $MY_DIR/FILE.*.xml &> /dev/null, Таким образом, пользователь не увидит дополнительный вывод.