На самом деле, for i in *; do something; done
рассматривает каждое имя файла правильно, за исключением того, что имена файлов, которые начинаются с a .
исключены из подстановочного соответствия. Соответствовать всем файлам (кроме .
и ..
) портативно, соответствие * .[!.]* ..?*
и пропустите любой несуществующий файл, следующий из шаблона несоответствия, оставляемого неповрежденным.
При испытании проблем это, вероятно, потому что Вы не заключили в кавычки $i
правильно позже. Всегда помещайте двойные кавычки вокруг подстановок переменных и управляйте заменами: "$foo"
, "$(cmd)"
если Вы не предназначаете полевое разделение и globbing для случая.
Если необходимо передать имя файла внешней команде (Вы не делаете, здесь), осторожны это echo "$foo"
не всегда печатает $foo
буквально. Несколько оболочек выполняют расширение обратной косой черты и несколько значений $foo
начало -
будет рассматриваться как опцию. Безопасный и совместимый POSIX способ распечатать строку точно
printf '%s' "$foo"
или printf '%s\n' "$foo"
добавить новую строку в конце. Другая вещь не упустить состоит в том, что замена команды удаляет запаздывание новых строк; если необходимо сохранить новые строки, возможный прием должен добавить несимвол новой строки к данным, удостоверьтесь, что преобразование сохраняет этот символ, и наконец усеките этот символ. Например:
mangled_file_name="$(printf '%sa' "$file_name" | tr -sc '[:alnum:]-+_.' '[_*]')"
mangled_file_name="${mangled_file_name%a}"
Для извлечения md5sum файла постарайтесь не иметь имя файла в md5sum
вывод, так как это будет мешать разделять. Передайте данные md5sum
стандартный вход.
Обратите внимание что md5sum
команда не находится в POSIX. Несколько вариантов Unix имеют md5
или ничто вообще. cksum
POSIX, но склонный к коллизии.
Посмотрите Захват расширения в имени файла о том, как получить расширение файла.
Давайте соединим все это (непротестированное). Все здесь работает под любой оболочкой POSIX; Вы могли получить немного, но не очень, от функций удара.
for old_name in * .[!.]* ..?*; do
if ! [ -e "$old_name" ]; then continue; fi
hash=$(md5sum <"$old_name")
case "$old_name" in
*.*.gz|*.*.bz2) # double extension
ext=".${old_name##*.}"
tmp="${old_name%.*}"
ext=".${old_name##*.}$ext";;
?*.*) ext=".${old_name##*.}";; # simple extension
*) ext=;; # no extension
esac
mv -- "$old_name" "$hash$ext"
done
Обратите внимание, что я не рассматривал случай, где уже существует конечный файл указанным именем. В частности, если у Вас будут существующие файлы, имя которых похоже на Вашу принятую конвенцию, но где часть контрольной суммы не соответствует содержанию файла и вместо этого соответствует части некоторого другого файла с тем же расширением, что происходит, будет зависеть от относительного лексикографического порядка имен файлов.
Можно сделать это в цикле оболочки. Код ниже должен работать со всеми видами странных путей с дополнительными наклонными чертами; если все Ваши пути имеют форму /foo/bar
, можно сойти с рук что-то более простое.
split_common_prefix () {
path1=$1
path2=$2
common_prefix=
## Handle initial // specially
case $path1 in
//[!/]*) case $path2 in
//[!/]*) common_prefix=/ path1=${path1#/} path2=${path2#/};;
*) return;;
esac;;
/*) case $path2 in
/*) :;;
*) return;;
esac;;
*) case $path2 in /*) return;; esac;;
esac
## Normalize multiple slashes
trailing_slash1= trailing_slash2=
case $path1 in */) trailing_slash1=/;; esac
case $path2 in */) trailing_slash2=/;; esac
path1=$(printf %s/ "$path1" | tr -s / /)
path2=$(printf %s/ "$path2" | tr -s / /)
if [ -z "$trailing_slash1" ]; then path1=${path1%/}; fi
if [ -z "$trailing_slash2" ]; then path2=${path2%/}; fi
## Handle the complete prefix case (faster, necessary for equality and
## for some cases with trailing slashes)
case $path1 in
"$path2")
common_prefix=$path1; path1= path2=
return;;
"$path2"/*)
common_prefix=$path2; path1=${path1#$common_prefix} path2=
return;;
esac
case $path2 in
"$path1"/*)
common_prefix=$path1; path1= path2=${path2#$common_prefix}
return;;
esac
## Handle the generic case
while prefix1=${path1%%/*} prefix2=${path2%%/*}
[ "$prefix1" = "$prefix2" ]
do
common_prefix=$common_prefix$prefix1/
path1=${path1#$prefix1/} path2=${path2#$prefix1/}
done
}
С другой стороны, определите самый длинный общий префикс двух строк и обрежьте его к его последнему /
символ (кроме тех случаев, когда общий префикс состоит только из наклонных черт).
Можно вычислить самую длинную общую ведущую подстроку списка строк с этим:
sed -e '1{h;d;}' -e 'G;s,\(.*\).*\n\1.*,\1,;h;$!d'
Который, например, для:
/abc/bcd/cdf
/abc/bcd/cdf/foo
/abc/bcd/chi/hij
/abc/bcd/cdd
возвраты:
/abc/bcd/c
Ограничить его компонентами контура:
sed -e 's,$,/,;1{h;d;}' -e 'G;s,\(.*/\).*\n\1.*,\1,;h;$!d;s,/$,,'
(возвраты /abc/bcd
на образце выше).
Нет такого инструмента к моему знанию. Однако можно легко записать такую программу, так как необходимо сделать, определяют самую длинную группу компонентов.
Пример "острота":
echo /abc/bcd/cdf | awk -vpath=/abc/bcd/chi/hij -F/ '{ OFS="\n";len=0; split(path, components); for (i=1; i<=NF; i++) if($i == components[i])len+=1+length($i);else break;print substr($0, 1, len - 1), substr($0, len + 1), substr(path, len + 1);exit;}
Отформатированная версия с комментариями:
$ cat longest-path.awk
#!/usr/bin/awk -f
BEGIN {
FS="/"; # split by slash
}
{
len=0; # initially the longest path has length 1
split(path, components); # split by directory separator (slash)
for (i=1; i<=NF; i++) { # loop through all path components
if ($i == components[i]) {
len += 1 + length($i);
} else {
break; # if there is a mismatch, terminate
}
}
print substr($0, 1, len - 1); # longest prefix minus slash
print substr($0, len + 1); # remainder stdin
print substr(path, len + 1); # remainder path
exit; # only the first line is compared
}
$ echo /abc/bcd/cdf | ./longest-path.awk -vpath=/abc/bcd/chi/hij
/abc/bcd
cdf
chi/hij
Вот недоброкачественная продукция, которая, кажется, отвечает на вопрос, хорошо используя обычный (стандарт?) средства unix/linux согласно просьбе (хорошо... Я попробовал его только на моем Linux Mageia).
#!/bin/sh
# Compute absolute pathnames common prefix and decompose second one
# Author Babou 2013/05/27 on http://unix.stackexchange.com/questions/67078/
first=`realpath -ms "$1"`
rel=`realpath -ms --relative-to="$1" "$2" | rev`
while [ `basename "$rel"` == '..' ]
do
first=`dirname "$first"`
rel=`dirname "$rel"`
done
echo $first + `echo $rel | rev`
И мой набор тестов:
./prefix /abc/bcd/cdf /abc/bcd/chi/hij
./prefix "/abc/bcd/cdf" "/abc/bcd/chi/hij"
./prefix "/ab c/bcd/cdf" "/ab c/bcd/chi/hij"
./prefix "/abc/bcd/cdf" "/abc/bcd/chi/h ij"
./prefix "/" "/"
./prefix "/abc/bcd/" "/abc/bcd/chi/hij"
./prefix "/abc/bcd/cdf" "/abc/bcd/"
./prefix "/abc///zzz/../bcd/cdf" "///abc/bcd//chi/h i j/"
./prefix "/abèc/bcd/cdf" "/abèc/bcd/"
два примера:
$ ./prefix "/abc///zzz/../bcd/cdf" "///abc/bcd//chi/h i j/"
/abc/bcd + chi/h i j
$ ./prefix "/abèc/bcd/cdf" "/abèc/bcd/"
/abèc/bcd + .
Если Вы требуете анализировать оба пути, можно или изменить сценарий или применить его дважды, изменив порядок или аргументы.
Я не слишком доволен именами переменных..., но мой первый плохой класс в программировании происходил из-за неудавшегося альфа-преобразования (одно возникновение, о котором забывают). Таким образом, я оставляю его, как.
P.S. Можно хотеть объединить презентацию относительного пути (вторая часть разложения) когда пустой: это может стать или как "/" в одном случае, когда оба пути просто "/".
realpath
ни в коем случае не стандартная утилита. Версия Debian/Ubuntu только втянута как зависимость несколькими пакетами и не имеет a -m
опция. Утилита GNU для канонизации путей readlink
, который также существует (с меньшим количеством опций) на *BSD.
– Gilles 'SO- stop being evil'
28.05.2013, 15:48
realpath
утилита GNU с авторским правом FSF, и это действительно имеет a -m
опция. Но Ваш комментарий помог мне заметить, что я забыл другую опцию, -s
, предотвратить расширение символьной ссылки. Без -s
опция, это делает то же задание на канонических путях.
– babou
28.05.2013, 22:32
realpath
была сторонняя утилита (или скорее было несколько утилит с тем именем), с меньшим количеством опций.
– Gilles 'SO- stop being evil'
28.05.2013, 22:37
GNU coreutils 8.15 January 2012 REALPATH(1)
– babou
28.05.2013, 22:48
Stéphane Chazelas уже показал решение, основанное на sed -. Я наткнулся на немного другое выражение sed от ack , которое я настраиваю ниже, чтобы ответить на этот вопрос. В частности, я ограничиваю его компонентами пути и обрабатываю возможность новых строк в компонентах пути. Затем я демонстрирую его использование для разложения спецификаций пути на самые длинные общие ведущие компоненты пути + оставшиеся компоненты пути .
Начнем с выражения ack для sed(Я переключил его на синтаксис ERE ①):
sed -E '$!{N;s/^(.*).*\n\1.*$/\1\n\1/;D;}' <<"EOF'
/abc/bcd/cdf
/abc/bcd/cdf/foo
/abc/bcd/chi/hij
/abc/bcd/cdd
EOF
⇒ /abc/bcd/c
, как и ожидалось. ✔️
Чтобы ограничить его компонентами пути:
sed -E '$!{N;s|^(.*/).*\n\1.*$|\1\n\1|;D;};s|/$||' <<'EOF'
/abc/bcd/cdf
/abc/bcd/cdf/foo
/abc/bcd/chi/hij
/abc/bcd/cdd
EOF
⇒ /abc/bcd
, как и ожидалось. ✔️
В целях тестирования мы будем использовать этот массив спецификаций пути:
a=(
$'/a\n/b/\nc d\n/\n\ne/f'
$'/a\n/b/\nc d\n/\ne/f'
$'/a\n/b/\nc d\n/\ne\n/f'
$'/a\n/b/\nc d\n/\nef'
)
Посмотрев, мы можем увидеть, что самый длинный общий ведущий компонент пути равен:
$'/a\n/b/\nc d\n'
Это можно вычислить и зафиксировать в переменной с помощью следующего:
longest_common_leading_path_component=$(
printf '%s\0' "${a[@]}" \
| sed -zE '$!{N;s|^(.*/).*\x00\1.*$|\1\x00\1|;D;};s|/$||' \
| tr \\0 x # replace trailing NUL with a dummy character ②
)
# Remove the dummy character
longest_common_leading_path_component=${longest_common_leading_path_component%x}
# Inspect result
echo "${longest_common_leading_path_component@Q}" # ③
Результат:
$'/a\n/b/\nc d\n'
как и ожидалось. ✔️
Продолжая наш тестовый пример, мы теперь покажем, как разложить спецификации пути на самые длинные общие ведущие компоненты пути + оставшиеся компоненты пути со следующими:
for e in "${a[@]}"; do
remainder=${e#"$longest_common_leading_path_component/"}
printf '%-26s -> %s + %s\n' \
"${e@Q}" \
"${longest_common_leading_path_component@Q}" \
"${remainder@Q}"
done
Результат:
$'/a\n/b/\nc d\n/\n\ne/f' -> $'/a\n/b/\nc d\n' + $'\n\ne/f'
$'/a\n/b/\nc d\n/\ne/f' -> $'/a\n/b/\nc d\n' + $'\ne/f'
$'/a\n/b/\nc d\n/\ne\n/f' -> $'/a\n/b/\nc d\n' + $'\ne\n/f'
$'/a\n/b/\nc d\n/\nef' -> $'/a\n/b/\nc d\n' + $'\nef'
① Я всегда добавляю параметр -E
в sed и grep, чтобы переключить их на синтаксис ERE для лучшей согласованности с другими инструментами/языками, которые я использую, например, awk, bash, perl, javascript и Джава.
② Чтобы сохранить любые завершающие символы новой строки в этой подстановке команд, мы использовали обычную технику добавления фиктивного символа, который впоследствии обрезается. Мы объединили удаление завершающего NUL с добавлением фиктивного символа (, который мы выбралиx
)за один шаг, используя tr \\0 x
.
③ Расширение ${parameter@Q}
приводит к «строке, которая является значением параметра в кавычках в формате, который можно повторно использовать в качестве входных данных». – Справочное руководство по bash . Требуется bash 4.4+(обсуждение). В противном случае вы можете проверить результат, используя один из следующих:
printf '%q' "$longest_common_leading_path_component"
printf '%s' "$longest_common_leading_path_component" | od -An -tc
od -An -tc < <(printf %s "$longest_common_leading_path_component")
od -An -tc <<<$longest_common_leading_path_component # ④
④ Имейте в виду, что здесь -строки добавляют новую строку(обсуждение).
/
как общий префикс/
и/
, какова проблема? И какова проблема с пробелами? – Gilles 'SO- stop being evil' 28.05.2013, 15:42/
и/
. Уверенный – babou 28.05.2013, 17:02$ split_common_prefix / /; echo $?; echo "$common_prefix"
→0
/
– Gilles 'SO- stop being evil' 28.05.2013, 20:14