Этот сценарий оболочки в основном работа других людей. Он прошел несколько итераций, и я немного подправил его, одновременно пытаясь полностью понять, как он работает. Я думаю, что понимаю это сейчас, но у меня нет уверенности, чтобы значительно изменить это самостоятельно и рискую потерять данные, когда я запускаю измененную версию. Поэтому я был бы признателен за советы экспертов по улучшению этого скрипта.

Изменения, которые я ищу:

  1. сделайте его еще более устойчивым к любым странным именам файлов, если это возможно. В настоящее время он обрабатывает пробелы в именах файлов, но не переводит строки. Я могу жить с этим (потому что я пытаюсь найти любые имена файлов с символами новой строки и избавиться от них).
  2. сделать его более осмысленным в отношении того, какой файл сохраняется в качестве фактического содержимого inode, а какие файлы становятся ссылками sym. Я хотел бы иметь возможность сохранить файл, который является либо: а) кратчайшим путем, б) самым длинным путем, либо в) имеет имя файла с наибольшим количеством буквенных символов (которое, вероятно, будет наиболее описательным именем).
  3. разрешите ему читать каталоги для обработки либо из параметров, переданных в файл, либо из файла.
  4. При желании, запишите длинный список всех изменений и / или всех файлов, которые не были обработаны.

Из всех них, № 2 является наиболее важным для меня сейчас. Мне нужно обработать некоторые файлы с ним, и мне нужно улучшить способ выбора файлов для преобразования в символические ссылки. (Я попытался использовать такие вещи, как опция поиска -depth без успеха.)

Вот текущий скрипт:

#!/bin/bash

# clean up known problematic files first.
## find /home -type f -wholename '*Icon*
## *' -exec rm '{}' \;

# Configure script environment
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
set -o nounset
dir='/SOME/PATH/HERE/'

# For each path which has multiple links
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# (except ones containing newline)
last_inode=
while IFS= read -r path_info
do
   #echo "DEBUG: path_info: '$path_info'"
   inode=${path_info%%:*}
   path=${path_info#*:}
   if [[ $last_inode != $inode ]]; then
       last_inode=$inode
       path_to_keep=$path
   else
       printf "ln -s\t'$path_to_keep'\t'$path'\n"
       rm "$path"
       ln -s "$path_to_keep" "$path"
   fi
done < <( find "$dir" -type f -links +1 ! -wholename '*
*' -printf '%i:%p\n' | sort --field-separator=: )

# Warn about any excluded files
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
buf=$( find "$dir" -type f -links +1 -path '*
*' )
if [[ $buf != '' ]]; then
    echo 'Some files not processed because their paths contained newline(s):'$'\n'"$buf"
fi

exit 0

1 ответ1

2

1.

Одно простое изменение, чтобы не умирать для имен файлов, которые начинаются с - это добавить -- (означает «теперь все опции заданы, остались только позиционные аргументы») до начала аргументов имени файла, например

rm -- "$path"
ln -s -- "$path_to_keep" "$path"

и так далее.


2.

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

numberofalnum=$(printf -- "$path" | tr -cd [:alnum:] | wc -m)

Чтобы подсчитать глубину пути, вы можете попытаться просто посчитать вхождения / в названии файла. Предостережение может заключаться в том, что /home///daniel эквивалентен /home/daniel , но find не будет выводить ненужные множественные слэши, так что все будет в порядке.

depth=$(printf -- "$path" | tr -cd / | wc -m)

Можно также свернуть несколько косых черт, запустив tr -s / after printf . Таким образом, объединение -s , -c и -d таким способом в одном вызове не представляется возможным.

В этом случае, поскольку find уже используется в сценарии подобным образом, простое добавление поля : отделено в выводе -printf с %d приведет к прямой печати глубины, как указано ниже в комментарии.


3a.

Чтобы прочитать каталоги как аргументы из командной строки, посмотрите этот минимальный фрагмент:

#!/bin/sh
i=0
while [ $# -ne 0 ]; do
    printf -- 'Argument %d: %s\n' "${i}" "${1}"
    i=$((i+1))
    shift
done

($i просто счетчик, чтобы показать вам, что происходит)

Если вы заключите свою логику в такой цикл while, вы можете получить доступ к первому аргументу как ${1} , затем использовать shift который выталкивает первый элемент из списка аргументов, а затем повторять итерацию, и теперь ${1} является первоначально вторым аргумент. Делайте это, пока аргумент count $# не равен 0.


3b.

Чтобы прочитать аргументы из файла, вместо этого оберните его как

#!/bin/sh
i=1
while read line; do
    printf -- 'Argument %d: %s\n' "${i}" "${line}"
    i=$((i+1))
    shift
done < "${1}"

Совет: вместо того, чтобы просто увеличивать отступ и оборачивать всю файловую логику таким образом, создайте функции текущей логики и вызовите их в конце скрипта. Это позволит вам легко выбирать между предоставлением каталогов в качестве аргументов или чтением их из файла без дублирования кода в вашем скрипте.


4.

добавлять

printf 'My descriptive log message for path %s\n' "${path}" >> "${logfile}"

в логических блоках, где вы решили действовать или нет. Установите $logfile ранее для желаемого пути журнала.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .