Xargs для извлечения имени файла

Я хотел бы найти все файлы .html в папке и добавить [file](./file.html) в другой файл с именем index.md . Я попробовал следующую команду:

 ls | awk "/\.html$/" | xargs -0 -I @@ -L 1 sh -c 'echo "[${@@%.*}](./@@)" >> index.md' 

Но он не может заменить @@ внутри команды? Что я делаю неправильно?

Примечание: имя файла может содержать допустимые символы, такие как пробел


Разъяснение:

index.md будет каждая строка с [file](./file.html) где file – это фактическое имя файла в папке

Просто делать:

 for f in *.html; do printf '%s\n' "[${f%.*}](./$f)"; done > index.md 

Используйте set -o nullglob ( zsh , yash ) или shopt -s nullglob ( bash ) для *.html чтобы развернуть ничего, вместо *.html (или сообщить об ошибке в zsh ), когда нет html файла. С zsh вы также можете использовать *.html(N) или ksh93 ~(N)*.html .

Или с одним вызовом printf с помощью zsh :

 files=(*.html) rootnames=(${files:r}) printf '[%s](./%s)\n' ${basenames:^files} > index.md 

Обратите внимание, что в зависимости от того, какой синтаксис уценки вы используете, вам, возможно, придется HTML-кодировать часть заголовка и URI-кодировать часть URI, если имена файлов содержат некоторые проблемные символы. Несоблюдение этого требования может даже привести к появлению формы уязвимости XSS в зависимости от контекста. С ksh93 вы можете сделать это с:

 for f in *.html; do title=${ printf %H "${file%.*}"; } title=${title//$'\n'/"
"} uri=${ printf '%#H' "$file"; } uri=${uri//$'\n'/%0A} printf '%s\n' "[$title]($uri)" done > index.md

Где %H ¹ выполняет кодировку HTML, а %#H – кодировку URI, но нам все равно нужно обращаться к символам новой строки отдельно.

Или с perl :

 perl -MURI::Encode=uri_encode -MHTML::Entities -CLSA -le ' for (<*.html>) { $uri = uri_encode("./$_"); s/\.html\z//; $_ = encode_entities $_; s:\n:
:g; print "[$_]($uri)" }'

Использование
для символов новой строки. Возможно, вы захотите использовать ␤ вместо этого или, в более общем случае, выбрать альтернативную форму представления непечатных символов.

В вашем коде есть несколько ошибок:

  • парсинг вывода ls
  • использовать $ чтобы быть буквальным внутри двойных кавычек
  • Использование awk для чего-то, что может сделать grep (не само по себе, а излишне)
  • используйте xargs -0 если входные данные не разделены NUL
  • -I конфликтую с -L 1 . -L 1 – запускать одну команду для каждой строки ввода, но каждое слово в строке передается как отдельные аргументы, в то время как -I @@ запускает одну команду для каждой строки ввода с полной строкой (за исключением завершающих пробелов и цитированием по-прежнему обработано) используется для замены @@ .
  • использование {} внутри аргумента кода sh ( уязвимость внедрения команд )
  • В sh переменная в ${var%.*} Является именем переменной , она не будет работать с произвольным текстом.
  • использовать echo для произвольных данных.

Если вы хотите использовать xargs -0 , вам нужно что-то вроде:

 printf '%s\0' * | grep -z '\.html$' | xargs -r0 sh -c ' for file do printf "%s\n" "[${file%.*}](./$file)" done' sh > file.md 
  • Замена ls на printf '%s\0' * для получения вывода, разделенного NUL
  • awk с grep -z (расширение GNU) для обработки этого вывода, разделенного NUL
  • xargs -r0 (расширения GNU) без -n / -L / -I , потому что пока мы создаем sh , мы можем также обработать как можно больше файлов
  • xargs передает слова в качестве дополнительных аргументов в sh (которые становятся позиционными параметрами внутри встроенного кода), а не внутри аргумента кода.
  • Это означает, что мы можем легче хранить их в переменных (здесь for file do который по умолчанию переходит по позиционным параметрам), поэтому мы можем использовать оператор раскрытия параметра ${param%pattern} .
  • используйте printf вместо echo .

Само собой разумеется, что не имеет смысла использовать это вместо выполнения цикла for непосредственно над файлами *.html как в верхнем примере.


Seem В моей версии ksh93 это не работает должным образом для многобайтовых символов (ksh93u + в системе GNU)

Не разбирайся .
Для этого вам не нужны xargs , вы можете использовать find -exec .

попробуй это,

 find . -maxdepth 1 -type f -name "*.html" -exec \ sh -c 'f=$(basename "$1"); echo "[${f%.*}]($1)" >> index.md' sh {} \; 

Если вы хотите использовать xargs , используйте эту очень похожую версию:

 find . -maxdepth 1 -type f -name "*.html" -print0 | \ xargs -0 -I{} sh -c 'f=$(basename "$1"); echo "[${f%.*}]($1)" >> index.md' sh {} \; 

Другой способ без запуска xargs или -exec :

 find . -maxdepth 1 -type f -name "*.html" -printf '[%f](./%f)\n' \ | sed 's/\.html\]/]/' \ > index.md 

Вы действительно нуждаетесь в xargs ?

 ls *.html | perl -pe 's/.html\n//;$_="[$_](./$_.html)\n"' 

(Если у вас более 100000 файлов):

 printf "%s\n" *.html | perl -pe 's/.html\n//;$_="[$_](./$_.html)\n"' 

или (медленнее, но короче):

 for f in *.html; do echo "[${f%.*}](./$f)"; done