tee
может это сделать:
docker exec -i my_container tee file < file_on_host
-i
необходим для работы stdin.
Он также выведет файл, чего можно избежать, перенаправив на хост / dev / null
:
docker exec -i my_container tee file < file_on_host > /dev/null
Если вам нужно обрабатывать только пробелы и табуляции (, а не встроенные символы новой строки ), вы можете использоватьmapfile
(или его синонимreadarray
)для чтения в массив, например. дано
$ ls -1
file
other file
somefile
, затем
$ IFS= mapfile -t files < <(find. -type f)
$ select f in "${files[@]}"; do ls "$f"; break; done
1)./file
2)./somefile
3)./other file
#? 3
./other file
Если вам действительно нужно обрабатывать новые строки, а ваша bash
версия предоставляет -нуль-разделительmapfile
1 , вы можете изменить это на IFS= mapfile -t -d '' files < <(find. -type f -print0)
. В противном случае соберите эквивалентный массив из вывода find
с нулевым разделителем -, используя цикл read
:
$ touch $'filename\nwith\nnewlines'
$
$ files=()
$ while IFS= read -r -d '' f; do files+=("$f"); done < <(find. -type f -print0)
$
$ select f in "${files[@]}"; do ls "$f"; break; done
1)./file
2)./somefile
3)./other file
4)./filename
with
newlines
#? 4
./filename?with?newlines
1 опция -d
была добавлена к mapfile
в bash
версии 4.4 iirc
Вы не можете установить переменную перед циклической конструкцией, но вы можете установить ее перед условием. Вот фрагмент из справочной страницы:
The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments, as described above in PARAMETERS.
(Цикл не является простой командой .)
Вот часто используемая конструкция, демонстрирующая сценарии неудачи и успеха:
IFS=$'\n' while read -r x; do...; done </tmp/file # Failure
while IFS=$'\n' read -r x; do...; done </tmp/file # Success
К сожалению, я не вижу способа внедрить измененный IFS
в конструкцию select
, в то время как он влияет на обработку связанного $(...)
. Однако ничто не мешает установить IFS
вне цикла :
IFS=$'\n'; while read -r x; do...; done </tmp/file # Also success
и я вижу, что эта конструкция работает сselect
:
IFS=$'\n'; select file in $(find -type f -name 'file*'); do echo "$file"; break; done
При написании защитного кода я бы рекомендовал либо запускать предложение в подоболочке, либо IFS
и SHELLOPTS
сохранять и восстанавливать по блоку:
OIFS="$IFS" IFS=$'\n' # Split on newline only
OSHELLOPTS="$SHELLOPTS"; set -o noglob # Wildcards must not expand twice
select file in $(find -type f -name 'file*'); do echo $file; break; done
IFS="$OIFS"
[[ "$OSHELLOPTS" !~ noglob ]] && set +o noglob
Возможно, я не в своей юрисдикции, но, может быть, вы можете начать с чего-то вроде этого, по крайней мере, у него нет проблем с пробелами:
find -maxdepth 1 -type f -printf '%f\000' | {
while read -d $'\000'; do
echo "$REPLY"
echo
done
}
Чтобы избежать любых возможных ложных предположений, как отмечено в комментариях, имейте в виду, что приведенный выше код эквивалентен:
find -maxdepth 1 -type f -printf '%f\0' | {
while read -d ''; do
echo "$REPLY"
echo
done
}
В этом ответе есть решения для любого типа файлов. С новыми строками или пробелами.
Есть решения для недавнего bash, а также для древнего bash и даже для старых оболочек posix.
Дерево, указанное ниже в этом ответе [1] , используется для тестов.
Легко заставить select
работать либо с массивом:
$ dir='deep/inside/a/dir'
$ arr=( "$dir"/* )
$ select var in "${arr[@]}"; do echo "$var"; break; done
Или с позиционными параметрами:
$ set -- "$dir"/*
$ select var; do echo "$var"; break; done
Таким образом, единственная реальная проблема состоит в том, чтобы получить «список файлов» (, правильно разделенный )внутри массива или внутри позиционных параметров. Продолжай читать.
Я не вижу проблемы с bash, о которой вы сообщаете. Bash может выполнять поиск внутри заданного каталога:
$ dir='deep/inside/a/dir'
$ printf '<%s>\n' "$dir"/*
<deep/inside/a/dir/directory>
<deep/inside/a/dir/file>
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/zz last file>
Или, если вам нравится петля:
$ set -- "$dir"/*
$ for f; do printf '<%s>\n' "$f"; done
<deep/inside/a/dir/directory>
<deep/inside/a/dir/file>
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/zz last file>
Обратите внимание, что приведенный выше синтаксис будет корректно работать с любой (разумной )оболочкой (не csh по крайней мере ).
Единственное ограничение приведенного выше синтаксиса — переход в другие каталоги.
Но Баш мог это сделать:
$ shopt -s globstar
$ set -- "$dir"/**/*
$ for f; do printf '<%s>\n' "$f"; done
<deep/inside/a/dir/directory>
<deep/inside/a/dir/directory/file>
<deep/inside/a/dir/directory/file name>
<deep/inside/a/dir/directory/file with a
newline>
<deep/inside/a/dir/directory/zz last file>
<deep/inside/a/dir/file>
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/zz last file>
Чтобы выбрать только некоторые файлы (, подобные тем, которые заканчиваются в файле ), просто замените*:
$ set -- "$dir"/**/*file
$ printf '<%s>\n' "$@"
<deep/inside/a/dir/directory/file>
<deep/inside/a/dir/directory/zz last file>
<deep/inside/a/dir/file>
<deep/inside/a/dir/zz last file>
Когда вы размещаете в заголовке «пространство-сейф », я предполагаю, что вы имели в виду «надежный ».
Самый простой способ быть устойчивым к пробелам (или символам новой строки )— отказаться от обработки ввода, содержащего пробелы (или символы новой строки ).Очень простой способ сделать это в оболочке — выйти с ошибкой, если любое имя файла расширяется пробелом. Есть несколько способов сделать это, но наиболее компактным (и posix )(, но ограниченным содержимым одного каталога, включая имена suddirectories и избегая точечных -файлов ), является:
$ set -- "$dir"/file* # read the directory
$ a="$(printf '%s' "$@" x)" # make it a long string
$ [ "$a" = "${a%% *}" ] || echo "exit on space" # if $a has an space.
$ nl='
' # define a new line in the usual posix way.
$ [ "$a" = "${a%%"$nl"*}" ] || echo "exit on newline" # if $a has a newline.
Если используемое решение надежно в любом из этих пунктов, удалите тест.
В bash вложенные каталоги -можно было протестировать сразу с помощью описанного выше **.
Существует несколько способов включения точечных файлов, решение Posix:
set -- "$dir"/* "$dir"/.[!.]* "$dir"/..?*
Если по какой-либо причине необходимо использовать find, замените разделитель на NUL (0x00 ).
$ readarray -t -d '' arr < <(find "$dir" -type f -name file\* -print0)
$ printf '<%s>\n' "${arr[@]}"
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/directory/file name>
<deep/inside/a/dir/directory/file with a
newline>
<deep/inside/a/dir/directory/file>
<deep/inside/a/dir/file>
i=1 # lets start on 1 so it works also in zsh.
while IFS='' read -d '' val; do
arr[i++]="$val";
done < <(find "$dir" -type f -name \*file -print0)
printf '<%s>\n' "${arr[@]}"
Чтобы сделать правильное решение POSIX, в котором find не имеет разделителя NUL и нет ни -d
(, ни-a
)для чтения, нам нужен совершенно другой подход.
Нужно использовать комплекс -exec
from find с вызовом шелла:
find "$dir" -type f -exec sh -c '
for f do
echo "<$f>"
done
' sh {} +
Или, если требуется select (select является частью bash, а не sh):
$ find "$dir" -type f -exec bash -c '
select f; do echo "<$f>"; break; done ' bash {} +
1) deep/inside/a/dir/file name
2) deep/inside/a/dir/zz last file
3) deep/inside/a/dir/file with a
newline
4) deep/inside/a/dir/directory/file name
5) deep/inside/a/dir/directory/zz last file
6) deep/inside/a/dir/directory/file with a
newline
7) deep/inside/a/dir/directory/file
8) deep/inside/a/dir/file
#? 3
<deep/inside/a/dir/file with a
newline>
[1] Это дерево (\012 — это новые строки):
$ tree
.
└── deep
└── inside
└── a
└── dir
├── directory
│ ├── file
│ ├── file name
│ └── file with a \012newline
├── file
├── file name
├── otherfile
├── with a\012newline
└── zz last file
Можно построить с помощью этих двух команд:
$ mkdir -p deep/inside/a/dir/directory/
$ touch deep/inside/a/dir/{,directory/}{file{,\ {name,with\ a$'\n'newline}},zz\ last\ file}