
Я, возможно, знаю, как использовать readlink
для получения обычных файлов из SymLinks. У меня была эта очень простая идея заменить любой SymLink, $@
переданный моей функции, следующим образом:
for file in "$@"; do
[ -L "$file" ] && file=$(readlink -f "$file")
done
Примером может служить SymLink /etc/os-release -> ../usr/lib/os-release
.
Но если я сделаю это:
set -- '/etc/os-release'; for file in "$@"; do [ -L "$file" ] && file=$(readlink -f "$file"); done; echo "$@"
Я все еще получаю оригинальный путь SymLink, что .. разочаровывает. Я надеялся, что смогу напрямую изменить $@
, если это невозможно, есть ли какие-либо обходные пути?
решение1
Ваш исходный код:
for file in "$@"; do
[ -L "$file" ] && file=$(readlink -f -- "$file")
done
Проблема в том, что настройка file
в цикле не влияет на содержимое списка позиционных параметров.
Вместо этого вы можете использовать
for file in "$@"; do
[ -L "$file" ] && file=$(readlink -f -- "$file")
set -- "$@" "$file"
shift
done
Теперь мы сдвигаем записи с начала "$@"
, одну за другой, и добавляем возможно измененные записи в конец списка. Изменение $@
в for
цикле $@
не является проблемой, поскольку for
циклы всегда итерируют по статическому списку, т. е. список, который итерируется, оценивается один раз в начале цикла, только.
Цикл выше, как и циклы ниже, сбрасывает весь список позиционных параметров в каждой итерации. Дляогромныйсписок имен файлов, это может вскоре стать довольно медленным. Если в вашей оболочке есть массивы,Ответ Стивена Киттапоказывает один из способов его ускорения.
Цикл можно также записать в немного более короткой форме (вызывая readlink
каждый элемент),
for dummy do
set -- "$@" "$(readlink -f -- "$1")"
shift
done
Это используется readlink
в соответствии с вашим вопросом, хотя это и не утилита POSIX.
Обратите внимание, что если выводreadlink
заканчивается одним или несколькими переводами строк(кроме того, который добавлен для завершения последней строки вывода), они будут удалены. Это следствие подстановки команды. Если это проблема, добавьте искусственный завершающий символ в подстановку команды, а затем удалите его, как mosvy
предлагаетсяв комментариях:
for file do
[ -L "$file" ] && file=$(readlink -f -- "$file"; echo x)
set -- "$@" "${file%x}"
shift
done
решение2
Обратите внимание, что ни , readlink
ни не realpath
являются командами POSIX. В наборе инструментов командной строки POSIX нет realpath()
API -like. Здесь вместо readlink
(которого существуют различные несовместимые реализации) вы можете использовать , zsh
который также имеет realpath()
встроенную функциональность -like с его :P
модификатором. В вашем sh
скрипте:
eval "set -- $(zsh -c 'print -r ${(qq)argv:P}' zsh "$@")"
Хотя вы могли бы с таким же успехом написать свой сценарий zsh
изначально, где он был бы просто:
argv=($argv:P)
Или, если python3
он более доступен, чем zsh:
eval "set -- $(
LC_ALL=C python3 -c '
import sys, os, shlex
print(" ".join(map(shlex.quote, map(os.path.realpath, sys.argv[1:]))))' "$@")"