Назад | Перейти на главную страницу

Извлечь недостающие пути из массива путей bash

У меня есть массив путей:

paths=(
  /foo/exists1
  /foo/exists2
  /foo/missing1
)

Чтобы найти недостающие:

ls "${paths[@]}" 1>/dev/null

Показывает:

ls: нет доступа к '/ foo / missing1': нет такого файла или каталога

Хорошо. Теперь я хочу убрать это:

ls "${paths[@]}" 1>/dev/null | sed 's/ls: cannot access //' | sed 's/: No such file or directory//''

Но я получаю:

ls: нет доступа к '/ foo / missing1': нет такого файла или каталога
/ foo / существует1
/ foo / существует2

Так что sed не работает, также отображаются существующие файлы.

Почему это происходит (почему игнорируется 1>/dev/null), и как мне это исправить?

У тебя есть уже обнаружено непосредственная причина того, что ваш код не сделал того, что вы ожидали: ошибки ls сообщаются в stderr (как предложено POSIX), который не воспринимается как входной поток. Таким образом, вы получили смесь нормального вывода (который прошел без изменений вашим sed операторы) и stderr (который их обошел). Я не знаю почему твой ls выход менялся между звонками; перенаправление stdout в / dev / null должно иметь эффект удаления всех "нормальных" (существующих путей) из вывода. Исправление для этого не впихнуть stderr в stdout.

Постобработка вывода из ls - опасная идея, если вам нужен надежный сценарий. Одна хорошая статья по этой теме: "Почему вам не следует анализировать вывод ls (1)", доступный на сайте wooledge.org. Один подробный вопрос / ответ на сайте Unix & Linux касается некоторых проблем: Зачем не разбирать ls (а что делать вместо этого)?. В результате имена файлов UNIX могут содержать практически любой символ, включая пробелы, табуляции, новые строки, одинарные кавычки, двойные кавычки, экранированные одинарные кавычки и т. Д.! В качестве быстрых примеров рассмотрим каталоги с этими именами, все из которых совершенно законны:

  • "Нет такого файла" (mkdir "No such file")
  • «ls: нет доступа к 'foo': нет такого файла или каталога» (mkdir "ls: cannot access 'foo': No such file or directory")
  • "каталог

    с участием

    встроенный

    новые строки "(mkdir $'directory\nwith\nembedded\newlines')

Первый - это невинный каталог, ошибочно захваченный (из stdout) grep. Второй тоже был захвачен незаконно, но затем его исказили. совершенно другой путь - которые могут существовать, а могут и не существовать! -- посредством sed заявления. Третий - это один из примеров того, что происходит, когда вы передаете вывод ls в линейно-ориентированные программы; если каталог не существует, ls скажет это более чем в одной строке, что, вероятно, привело к тому, что вы получили два отдельных sed заявления!

Чтобы отличить «хорошие пути» - существующие и читаемые - от «плохих путей», я бы предложил перебирать массив и строить новые массивы для каждого.

for p in "${paths[@]}"
do
  if [ -r "$p" ]
  then
    goodpaths+=("$p")
  else
    badpaths+=("$p")
  fi
done

Затем вы можете делать все, что захотите, с каждым набором:

printf 'Good path: -->%s<--\n' "${goodpaths[@]}"
echo
printf 'Bad path: -->%s<--\n' "${badpaths[@]}"

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

Так надо перенаправить 2 к 1 затем grep:

  paths=(
    /foo/exists1
    /foo/exists2
    /foo/missing1
  )
  ls "${paths[@]}" 2>&1 \
    | grep 'No such file' \
    | sed 's/ls: cannot access '\''//' \
    | sed 's/'\'': No such file or directory//'