[/b/] [/d/] [/tu/] [/a/] [/ph/] [/wa/] [/cg/] [/t/] [/p/]
#!/bin/shCR=$(printf '\r')# file name: timestampfilename=$(date +%s)IFS="$CR"read -r delim # field delimiter boundaryread -r disp # Content-Disposition headerIFS=# skip leading empty lineswhile read -r line; do [ -z "$line" ] && break [ "$line" = "$CR" ] && breakdone# save received text into the $filenameIFS="$CR"while read -r line; do [ "$line" = "$delim"-- ] && break echo "$line"done > $filenameIFS=Это немного модифицированный кот из busybox/networking/http_post_upload.cgi.Вопрос. Как взять текст из нескольких полей? В цикле на 10-й строке проверка равенства "$line" = "$delim" не срабатывает.В приложенном zip.jpg рабочее файлохламилище, кот почти такой же. Но файлы принимаются, опять же, по одному.
#!/bin/sh
CR=$(printf '\r')
# file name: timestampfilename=$(date +%s)
IFS="$CR"read -r delim # field delimiter boundaryread -r disp # Content-Disposition headerIFS=
# skip leading empty lineswhile read -r line; do [ -z "$line" ] && break [ "$line" = "$CR" ] && breakdone
# save received text into the $filenameIFS="$CR"while read -r line; do [ "$line" = "$delim"-- ] && break echo "$line"done > $filenameIFS=
Это немного модифицированный кот из busybox/networking/http_post_upload.cgi.Вопрос. Как взять текст из нескольких полей? В цикле на 10-й строке проверка равенства "$line" = "$delim" не срабатывает.В приложенном zip.jpg рабочее файлохламилище, кот почти такой же. Но файлы принимаются, опять же, по одному.
Я чёт такое на авк писал.>Как взять текст из нескольких полей?В цикле и проверять content-length, ради тестов сделай себе echo.cgi.
Я чёт такое на авк писал.
>Как взять текст из нескольких полей?
В цикле и проверять content-length, ради тестов сделай себе echo.cgi.
>>201587> ради тестов сделай себе echo.cgiСделал.cat > dataenv > varsprintf 'HTTP/1.1 303 See Other\r\n'printf 'Location: /\r\n\r\n'
>>201587
> ради тестов сделай себе echo.cgi
Сделал.
cat > dataenv > varsprintf 'HTTP/1.1 303 See Other\r\n'printf 'Location: /\r\n\r\n'
#!/bin/shdelim=--${CONTENT_TYPE#multipart/form-data; boundary=}echo $delim > _delim # dbgCR=$(printf '\r')while read line; do [ "$line" = "$delim"-- ] && break [ "$line" = "$delim" ] && continue IFS="$CR" read -r disp name=${disp#Content-Disposition: form-data; name=\"} name=${name%?} IFS= while read -r _; do # ? [ -z "$_" ] && break [ "$_" = "$CR" ] && break done IFS="$CR" while read -r _; do [ "$line" = "$delim" ] && break [ "$line" = "$delim"-- ] && break echo "$_" done > $name IFS=doneprintf 'HTTP/1.1 303 See Other\r\n'printf 'Location: /\r\n\r\n'Вот. Почему-то не выходит из первого внутреннего цикла, всё до конца сохраняется в один файл. Никак не могу взять в толк, почему же.
delim=--${CONTENT_TYPE#multipart/form-data; boundary=}echo $delim > _delim # dbg
while read line; do [ "$line" = "$delim"-- ] && break [ "$line" = "$delim" ] && continue IFS="$CR" read -r disp name=${disp#Content-Disposition: form-data; name=\"} name=${name%?} IFS= while read -r _; do # ? [ -z "$_" ] && break [ "$_" = "$CR" ] && break done IFS="$CR" while read -r _; do [ "$line" = "$delim" ] && break [ "$line" = "$delim"-- ] && break echo "$_" done > $name IFS=done
printf 'HTTP/1.1 303 See Other\r\n'printf 'Location: /\r\n\r\n'
Вот. Почему-то не выходит из первого внутреннего цикла, всё до конца сохраняется в один файл. Никак не могу взять в толк, почему же.
#!/bin/shdelim="--${CONTENT_TYPE#multipart/form-data; boundary=}"CR=$(printf '\r')save() { IFS="$CR" read -r disp name=${disp#Content-Disposition: form-data; name=\"} name=${name%?} IFS= while read -r _; do [ -z "$_" ] && break [ "$_" = "$CR" ] && break done IFS="$CR" while read -r _; do [ "$_" = "$delim" ] && save [ "$_" = "$delim"-- ] && exit 0 echo "$_" done > $name}IFS="$CR"while read -r line; do [ "$line" = "$delim" ] && save [ "$line" = "$delim"-- ] && exit 0doneIFS=printf 'HTTP/1.1 303 See Other\r\n'printf 'Location: /\r\n\r\n'Удобные они, текстовые протоколы, всё-таки. Кажется, я победил.
delim="--${CONTENT_TYPE#multipart/form-data; boundary=}"CR=$(printf '\r')
save() { IFS="$CR" read -r disp name=${disp#Content-Disposition: form-data; name=\"} name=${name%?} IFS= while read -r _; do [ -z "$_" ] && break [ "$_" = "$CR" ] && break done IFS="$CR" while read -r _; do [ "$_" = "$delim" ] && save [ "$_" = "$delim"-- ] && exit 0 echo "$_" done > $name}
IFS="$CR"while read -r line; do [ "$line" = "$delim" ] && save [ "$line" = "$delim"-- ] && exit 0doneIFS=
Удобные они, текстовые протоколы, всё-таки. Кажется, я победил.
Спасибо, Новерь, без тебя бы ничего не вышло.
С текстовыми полями получается, а вот картинки сохранить — нетривиальная загогулина. Юниксовые программы (или мои о них знания) плохо подходят для обработки бинарных данных.Проще будет так: сохранить весь запрос, пришедший с файлового инпута multiple в один файл, просто cat. Затем выпарсить отдельные файлы, но:while read _ ... echo или printf — не подойдёт, испортит данные;dd не катит — размер каждого из файлов неизвестен;Нужно как-то найти разделитель, потом следующий, и сохранить то, что между ними. Я бы такую утилиту на сишке написал, но это не чистое решение.>>201587> проверять content-lengthОн один для всего запроса, а не для каждого из файлов. Почему это не добавили в CGI наряду с MIME-типом, я не знаю, было бы гораздо проще.Получается типа того…-----------------------------29995809218093749221856446032^MContent-Disposition: form-data; name="file1"; filename="..."^MContent-Type: application/octet-stream^M^M <--------- headers end with empty linefile contentsfile contentsfile contents^M <--------- extra empty line-----------------------------29995809218093749221856446032--^MВот если б только былоContent-Type: application/octet-stream; length="N"^M!
С текстовыми полями получается, а вот картинки сохранить — нетривиальная загогулина. Юниксовые программы (или мои о них знания) плохо подходят для обработки бинарных данных.Проще будет так: сохранить весь запрос, пришедший с файлового инпута multiple в один файл, просто cat. Затем выпарсить отдельные файлы, но:while read _ ... echo или printf — не подойдёт, испортит данные;dd не катит — размер каждого из файлов неизвестен;Нужно как-то найти разделитель, потом следующий, и сохранить то, что между ними. Я бы такую утилиту на сишке написал, но это не чистое решение.>>201587
> проверять content-length
Он один для всего запроса, а не для каждого из файлов. Почему это не добавили в CGI наряду с MIME-типом, я не знаю, было бы гораздо проще.Получается типа того…
-----------------------------29995809218093749221856446032^MContent-Disposition: form-data; name="file1"; filename="..."^MContent-Type: application/octet-stream^M^M <--------- headers end with empty linefile contentsfile contentsfile contents^M <--------- extra empty line-----------------------------29995809218093749221856446032--^M
Вот если б только было
Content-Type: application/octet-stream; length="N"^M
!
>>201657пошто панцу порвал дорогие же
# usage:# filescount /path/to/dir/* # count all files# filescount /path/to/dir/*/ # count all directoriesfilescount() { [ -e "$1" ] \ && printf '%s\n' "$#" \ || printf '%s\n' 0}Чтобы без $(ls -1 /path/to/dir | wc -l).
# usage:# filescount /path/to/dir/* # count all files# filescount /path/to/dir/*/ # count all directoriesfilescount() { [ -e "$1" ] \ && printf '%s\n' "$#" \ || printf '%s\n' 0}
Чтобы без $(ls -1 /path/to/dir | wc -l).
>>201867`find /path/to/dir -type f | wc -l``find /path/to/dir -type d | wc -l`?
>>201867
`find /path/to/dir -type f | wc -l``find /path/to/dir -type d | wc -l`
?
>>201868Если нужно посчитать файлы нерекурсивно, то можно обойтись без дополнительных процессов же. Здесь «файлы» — всё, кроме .., . и остальных скрытых. То есть, каталоги тоже.Если рекурсивно, то будет вроде тогоrecursive_filescount() { for f in $@; do [ -f "$f" ] && i=$((i+1)) [ -d "$f" ] && recursive_filescount "$f"/* done}i=0recursive_filescount path/to/dirprintf '%s\n' $i
>>201868Если нужно посчитать файлы нерекурсивно, то можно обойтись без дополнительных процессов же. Здесь «файлы» — всё, кроме .., . и остальных скрытых. То есть, каталоги тоже.Если рекурсивно, то будет вроде того
recursive_filescount() { for f in $@; do [ -f "$f" ] && i=$((i+1)) [ -d "$f" ] && recursive_filescount "$f"/* done}
i=0recursive_filescount path/to/dirprintf '%s\n' $i
>>201869#!/bin/sh recursive_filescount() { for f in $@; do [ -f "$f" ] && i=$((i+1)) [ -d "$f" ] && recursive_filescount "$f"/* done} if [ ! -d "$1" ]then echo "'$1' is not directory"fii=0recursive_filescount "$1"echo "$i"
>>201869
#!/bin/sh recursive_filescount() { for f in $@; do [ -f "$f" ] && i=$((i+1)) [ -d "$f" ] && recursive_filescount "$f"/* done}
if [ ! -d "$1" ]then echo "'$1' is not directory"fi
i=0recursive_filescount "$1"
echo "$i"
>>201869Алсо, он не считает директории у тебя.
>>201922Второй, рекурсивный, и не должен — там для этого проверка. Он в некоторых местах ошибается, даёт результат меньший чем find | wc -l.
>>201925Дело было в скрытых файлах и каталогах. find их обрабатывает, * — пропускает. Ещё и "$@" нужно закавычивать.#!/bin/shrecursive_filescount() { for f in "$@"; do [ -f "$f" ] && i=$((i+1)) [ -d "$f" ] && recursive_filescount "$f"/* done}[ ! -d "$1" ] && echo "'$1': not a directory" 2>&1i=0recursive_filescount "$1"echo $i
>>201925Дело было в скрытых файлах и каталогах. find их обрабатывает, * — пропускает. Ещё и "$@" нужно закавычивать.
recursive_filescount() { for f in "$@"; do [ -f "$f" ] && i=$((i+1)) [ -d "$f" ] && recursive_filescount "$f"/* done}
[ ! -d "$1" ] && echo "'$1': not a directory" 2>&1
i=0recursive_filescount "$1"echo $i
>>201927shopt -s dotglob
>>201927Алсо, зачем 2>&1? И почему exit не добавил в if [ ! -d ... ]? Я забыл просто.
>>201931> зачем 2>&1?Не зачем, а почему — от невысыпания.> почему exit не добавил в if [ ! -d ... ]?Мне показалось, ты решил так, что один файл есть — значит, не авария.>>201930> shopt -s dotglobЭто уже не POSIX. Есть только в bash, zsh (shopt -s globdots).
>>201931
> зачем 2>&1?
Не зачем, а почему — от невысыпания.
> почему exit не добавил в if [ ! -d ... ]?
Мне показалось, ты решил так, что один файл есть — значит, не авария.
>>201930
> shopt -s dotglob
Это уже не POSIX. Есть только в bash, zsh (shopt -s globdots).
>>201934>Мне показалось, ты решил так, что один файл есть — значит, не авария.На самом деле так и было, но как-то не имеет смысла.>Это уже не POSIXА GLOBIGNORE? Если он посиксный, то можно через .* и GLOBIGNORE='.:..' сделать.
>>201934
>Мне показалось, ты решил так, что один файл есть — значит, не авария.
На самом деле так и было, но как-то не имеет смысла.
>Это уже не POSIX
А GLOBIGNORE? Если он посиксный, то можно через .* и GLOBIGNORE='.:..' сделать.
https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/utilities/head.htmlhttps://pubs.opengroup.org/onlinepubs/9699919799.2008edition/utilities/tail.html> POSIX> tail -c> tail -c +10> no "head -c"
https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/utilities/head.htmlhttps://pubs.opengroup.org/onlinepubs/9699919799.2008edition/utilities/tail.html
> POSIX> tail -c> tail -c +10> no "head -c"
Вместо head/tail нельзя ли использовать sed или awk? Покрайней мере что-то вроде sed -n 1,3p покрасивее.
>>207101Думаю там возмущение было по поводу отсуствия у posix head опции -c, из-за чего для вывода первых n байт файла нужно использовать tail -c +n, что ни разу не интуитивно. В gnu версии head эта опция уже есть. Ну а в качестве замены можно и dd использовать.
>>207103"tail -c+n" не выводит первые байты, а пропускает: "tail -c+32" начинает вывод с 32-го байта и далее до конца. Это ещё более неинтуитивно.Очень странно. И в новой редакции (2017) его тоже нет.> Ну а в качестве замены можно и dd использовать.Придётся. Если писать скрипты для всех юниксов сразу.>>207101Использование "sed 10q" заместо "head" — целый мем.
>>207103"tail -c+n" не выводит первые байты, а пропускает: "tail -c+32" начинает вывод с 32-го байта и далее до конца. Это ещё более неинтуитивно.Очень странно. И в новой редакции (2017) его тоже нет.
> Ну а в качестве замены можно и dd использовать.
Придётся. Если писать скрипты для всех юниксов сразу.>>207101Использование "sed 10q" заместо "head" — целый мем.
Это мелочи по сравнению с тем, что в стандарте нету mount — TRUE-POSIX-OS не сможет загрузиться.
Ломающие новости! Аргументы в начале скрипта можно обрабатывать не только лесенкой if'ов.case $# in1) foo="$1" ;;2) foo="$1"; bar="$2" ;;*) usage ;;esacК тому же, в кейзах допустимы шаблоны — что и доказывает дефолтное правило сверху, со звёздочкой.case "$1" in[0-9]) n=$1 ;;[a-z]) usage ;;esac
Ломающие новости! Аргументы в начале скрипта можно обрабатывать не только лесенкой if'ов.case $# in
1) foo="$1" ;;2) foo="$1"; bar="$2" ;;*) usage ;;
esacК тому же, в кейзах допустимы шаблоны — что и доказывает дефолтное правило сверху, со звёздочкой.case "$1" in
[0-9]) n=$1 ;;[a-z]) usage ;;
esac
- wakaba 3.0.7 + futaba + futallaby -