Заполните 10000 путей в документе .txt и проверьте, существуют ли файлы… с помощью awk?

Как говорит ridiculous_fish, это, вероятно, связано с обработчиком команды not-found.

Вы можете попробовать добавить это предложение в ваш config.fish:

function __fish_default_command_not_found_handler --on-event fish_command_not_found
  functions --erase __fish_command_not_found_setup
  echo "'$argv' not found"
end

0
01.04.2018, 02:48
2 ответа

Если у вас установлена ​​GNU awkверсии 4 или более поздней, она имеет возможность загружать внешние модули, обеспечивающие функциональность, отсутствующую в стандартном awk или даже в расширенном GNU -awk. Он поставляется с набором модулей, в том числе с именем filefuncs. Модуль filefuncsвключает awkоболочку для системной statфункции, которую можно использовать для получения информации о файлах (, в том числе о том, существуют они или нет ).

Следующий скрипт awkзагружает модуль filefuncs, считывает каждую входную строку, проверяет 5-й столбец, чтобы решить, какой путь следует предварительно -ожидать для каждого входного имени файла, и проверяет, существует ли файл. Если это так, он печатает полный путь и имя файла на стандартный вывод. Если это не так, он печатает предупреждающее сообщение в stderr.

Ассоциативный массив paths(, также известный как «хэш» или «хешированный массив» )и путь по умолчанию, предварительно -ожидающий, — мои лучшие предположения о том, что вы намереваетесь. Отрегулируйте по мере необходимости. Это соответствует данным в предоставленных вами образцах (, даже очевидная ошибка с Media -> /Volumes/Logic ), а не то, что вы сказали в одном из ваших комментариев. Если ваш комментарий точен, то код можно упростить.

#!/usr/bin/awk -f

# this will only work with GNU awk >= version 4.0
@load "filefuncs"

BEGIN {
  FS=OFS="|";
  paths["default"] = "/Users/spazek/Pictures/Fotos Library.photoslibrary/Masters/";
  paths["Logic"] = "/Volumes/Logic/";
  paths["Media"] = "/Volumes/Logic/";
  paths["macOS"] = "/";
}

{ if ($5 in paths) {
    filename = paths[$5] $1;
  } else { # $5 not known in paths array, use a default
    filename = paths["default"] $1;
  }

  # try to stat the file. get the return code in variable 'rc' and error
  # string (if any) in 'error'.
  rc=stat(filename,fstat);
  error=ERRNO;   # oddly, ERRNO is a string, not a number.

  if (rc == -1) {  # return code of -1 is "No such file or directory"
    # print warning to stdout and skip to next input line
    print filename ": " error > "/dev/stderr"
    next;
  };

  # filename exists, do something with filename.
  print filename, $2, $3, $4, $5;
}

Сохранить как, например. ./exists.awk, сделайте его исполняемым с помощьюchmod +x(так же, как с помощью сценария оболочки ), и запустите его следующим образом:

./exists.awk /Users/spazek/Desktop/filelist1.txt

или направьте sqlite3 прямо в него:

sqlite3  -separator $'|' /Users/spazek/Desktop/xsystx/systphotos.db \
'select RKMaster.imagePath, RKMaster.name, RKMaster.uuid, RKMaster.fileIsReference, ( select RKVolume.name from RKVolume where RKVolume.modelId  = RKMaster.volumeId) from RKMaster' \
  |./exists.awk

Я не знаю, какая версия awkв настоящее время поставляется с Mac OS.Я подозреваю, что это, вероятно, либо BSD awk, либо какая-то древняя версия GNU awk, существовавшая до того, как Free Software Foundation перешла на использование лицензии GPLv3 (, поэтому Mac застряли на древней bashv3, а чем текущая bashверсия 4 -, это не потому, что Apple не может обновить bash, а потому, что они не будут . Используйте brew , если вам нужна более поздняя версия GNU bashили awk).

В любом случае, если у вас не установлен GNU awk >= v4.0, вы можете сделать то же самое с любой версией perl.

Следующий скрипт perlне использует никаких нестандартных -модулей или функций Perl, и даже не требует использования perlвстроенной -в stat()функции, поскольку в Perl есть операторы. аналогичны тем, что в shдля проверки существования файла. Здесь мы будем использовать оператор -e, который проверяет существование файла, как и в sh:

.
#!/usr/bin/perl

use strict;

# declare %paths hash
my %paths = (
  "default" => "/Users/spazek/Pictures/Fotos Library.photoslibrary/Masters/",
  "Media"   => "/Volumes/Logic/",
  "Logic"   => "/Volumes/Logic/",
  "macOS"   => "/",
);

# main loop, read in each line of input and process it.
while(<>) {
  chomp; # strip trailing linefeed from end-of-line
  my $filename='';  # declare $filename to belong to this scope

  # split input on "|" characters
  my ($path,$name,$id,$reference,$diskname) = split /\|/;

  if (defined($paths{$diskname})) {
    $filename = $paths{$diskname}. $path;
  } else {  # diskname not known in %paths hash, use a default
    $filename = paths{"default"}. $path;
  }

  if (! -e $filename) {
    # print warning to stderr and skip to next input line
    warn "$filename: No such file or directory\n";
    next;
  };

  # filename exists, do something with filename.
  print join('|', $filename, $id, $reference, $diskname), "\n";
}

Опять же, сохраните его как exists.plи сделайте исполняемым с помощью chmod +x. Выполнить как:

./exists.pl /Users/spazek/Desktop/filelist1.txt

Любой из этих двух сценариев будет в сотни или тысячи раз быстрее, чем сценарий оболочки, использующий while readили аналогичный цикл.

2
28.01.2020, 02:23

Я согласен с тем, что gawk4 или perl --или python --являются лучшим решением этой проблемы. Тем не менее, для дальнейшего использования и назидания, можно сделать ваш сценарий оболочки лучше или, по крайней мере, менее плохим.

Во-первых и самое главное, вам не нужно много раз запускатьawkилиcutдля разделения полей; пока ваши поля разделены одним символом, оболочка readможет сделать это за вас. Я не уверен, почему вы указали разделитель для awkкак [=\|], что означает либо знак равенства -, либо vert -правило -, также известное как -, канал, когда ваши данные из команды sqlite3, которая использует только правило vert -и никогда не использует знак равенства -. Таким образом, вы хотите начать с чего-то вроде:

 while IFS='=|' read var1 var2 var3 var4 var5; do... done <filelist1
 # change IFS='|' if you don't actually need to split on equal-sign 

 # could skip the first temp file, if you don't need it for anything else,
 # with either a pipeline (any shell):
 sqlite3... 'select...' | while IFS.. read...; do... done
 # or process substitution (only bash and some others):
 while IFS.. read...; do... done < <(sqlite3... 'select...')

Вероятно, лучше добавить опцию -rк read; данные вашего примера не содержали обратной косой черты, но если бы фактические данные когда-либо были, они были бы повреждены без -r. Конвейерный подход немного более переносим, ​​но в целом немного более рискован, потому что он может не работать, когда нужно установить var (s )или внести другие изменения оболочки (s ), например cdвнутри цикл , которые сохраняются после цикла --, но не вы.

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

while IFS.. read -r var1 var2 var3 var4 var5; do 
    if  [ "$var4" == 0 ]; then var1="/Users/spazek/Pictures/Fotos Library.photoslibrary/Masters/$var1"
    elif [ "$var5" == "macOS" ]; then var1="/$var1"
    else echo var1="/Volumes/$var5/$var1; fi
    test -f "$var1" || echo "Name = $var3 \n Path = $var1 \n"
done >~/Desktop/MissingPhotos.txt <filelist1 
# or options to avoid filelist1 per above

Наконец, я бы предложил использовать более осмысленные имена переменных, такие как path name idвместо var1и т. д., но это имеет значение только для людей, читающих сценарий, таких как вы через несколько месяцев; компьютеру все равно. Вы можете свободно выбирать имена переменных в нижнем регистре для переменных оболочки; по соглашению окружение переменные (т. е. переменные оболочки, которые экспортируются в программы, и дочерние оболочки )пишутся в верхнем регистре, но тогда вы должны быть немного осторожны, чтобы не конфликтовать с некоторыми специальными созданными переменными/окружениями. -в оболочке или стандартизировано для всей системы.

1
28.01.2020, 02:23

Теги

Похожие вопросы