Странное поведение bash script
Вот фрагмент:
var=`ls | shuf | head -2 | xargs cat | sed -e 's/\(.\)/\1\n/g' | shuf | tr -d '\n'`
Это выберет два случайных файла из текущего каталога, объединит их содержимое, перетасует их и присвоит результат var. Это работает отлично в большинстве случаев, но примерно раз в тысячах случаев, вместо этого только вывод ls
привязан к var (это не только выход, см. EDIT II). Что может быть объяснением?
Некоторые потенциально важные факты:
- каталог содержит не менее двух файлов
- есть только текстовые файлы в каталоге
- имена файлов не содержат пробелов
- файлы длиной от 5 до 1000 символов
- фрагмент является частью более крупного script, который параллельно запускал два экземпляра
- bash версия:
GNU bash, version 4.1.5(1)-release (i686-pc-linux-gnu)
- uname:
Linux 2.6.35-28-generic-pae #50-Ubuntu
РЕДАКТИРОВАТЬ: Я запускал фрагмент сам по себе несколько тысяч раз без ошибок. Затем я попытался запустить его с помощью других частей всего script. Здесь конфигурация, которая создает ошибки:
cd dir_with_text_files
var=`ls | shuf | head -2 | xargs cat | sed -e 's/\(.\)/\1\n/g' | shuf | tr -d '\n'`
cd ..
Существует несколько сотен строк script между cd
s, но это минимальная конфигурация для воспроизведения ошибки. Обратите внимание, что аномальный вывод привязывает к var выход текущего каталога, а не dir_with_text_files
.
EDIT II: Я смотрел результаты более подробно. Вывод ls
не отображается отдельно, он вместе с двумя перетасованными файлами (между их содержимым или после или перед ними неповрежденными). Но все становится лучше; позвольте мне создать сцену, чтобы поговорить о конкретных каталогах.
[~/projects/upload] ls -1
checked // dir
lines // dir, the files to shuffle are here
pages // also dir
proxycheck
singlepost
uploader
indexrefresh
t
tester
До сих пор я видел вывод ls
от upload
, но теперь я увидел результат ls */*
(также пробежал от upload
). Он был в форме "someMangledText ls
moreMangledText ls */*
finalBatchOfText". Возможно ли, что последовательность ls
, которая, несомненно, была сгенерирована, каким-то образом выполнена?
Ответы
Ответ 1
Здесь нет проблем.
Я также переписал бы сказанное выше:
sed 's:\(.\):\1\n:g' < <(shuf -e * | head -2 | xargs cat) | shuf | tr -d '\n'
Не используйте ls
для отображения содержимого каталога, используйте *
.
Кроме того, выполните некоторую отладку. Используйте shebang, а затем:
set -e
set -o pipefail
и запустите script следующим образом:
/bin/bash -x /path/to/script
и проверить вывод.
Вместо отладки всего script вы можете окружать только ту часть, которая кажется проблематичной с помощью -x
set -x
...code that may have problems...
set +x
чтобы выход фокусировался на этой части кода.
Кроме того, используйте параметр pipefail
.
Некоторые определения:
-
-e
: немедленно выйдите, если простая команда завершает работу с ненулевым статусом, если только команда, которая терпит неудачу, является частью списка команд сразу после некоторого времени или пока ключевое слово, часть теста в выражении if, не будет && или || list, или если статус возврата команды перевернут с помощью!. Ловушка в ERR, если установлена, выполняется до того, как оболочка выходит из -
-x
: печатать трассировку простых команд, команд, команд случая, команд выбора и арифметики для команд и их аргументов или связанных списков слов после их расширения и перед их выполнением. Значение переменной PS4 расширяется, и итоговое значение печатается перед командой и ее расширенными аргументами -
pipefail
: если установлено, возвращаемое значение конвейера - это значение последней (самой правой) команды для выхода с ненулевым статусом или ноль, если все команды в конвейере успешно завершены.
Ответ 2
В целях отладки вы также можете очистить среду с помощью env -i
и отфильтровать непечатаемые символы:
#!/usr/bin/env -i /bin/bash --
set -ef
set -o pipefail
unset IFS PATH LC_ALL
IFS=$' \t\n'
PATH="$(PATH=/bin:/usr/bin getconf PATH)"
LC_ALL=C
export IFS PATH LC_ALL
#var="$((find . -type f -maxdepth 1 -print0 | shuf -z -n 2 | xargs -0 cat) | sed -e 's/\(.\)/\1\n/g' | shuf | tr -d '\n')"
var="$((find . -type f -maxdepth 1 -print0 | shuf -z -n 2 | xargs -0 cat) | tr -cd '[[:print:]]' | grep -o '.' | shuf | tr -d '\n')"
Перед запуском script вы также можете отключить библиотеку readline GNU и! расширение истории стилей:
bash --noediting
set +H
Ответ 3
Основываясь на том, что вы говорите по вашим частотам отказов, и учитывая успех других тестов, выполненных плакатами выше, это звучит как проблема, которая может быть вызвана случайным сбоем смены каталога. Является ли каталог, к которому вы обращаетесь в этом script, установленном с удаленной машины случайно? Если это так, это может быть небольшой и временный сетевой сбой, который не обрабатывается должным образом. (Просто догадаться.)