Я думаю, что ответ @ilkkachu полезен и дает то, что вам нужно. Постараюсь объяснить с некоторыми подробностями, а заодно научусь пользоваться fifo.
Подготовьте два окна командной строки, w1 и w2 на одном компьютере
Создать прослушивание program
, я сделал шеллскрипт в w1
#!/bin/bash
while true
do
read string
if [ "${string:0:4}" == "Stop" ]
then
printf "Gotcha\n"
break
elif [ "$string" != "" ]
then
printf "$string "
else
sleep 3
fi
done
Подготовить fifo в w1
dir=$(mktemp -d)
mkfifo "$dir/mypipe"
Запустите программу и дайте ей дождаться ввода из fifo в w1
< "$dir/mypipe"./program
Ищите fifo и выводите в него несколько строк в w2
$ find /tmp -name mypipe 2>/dev/null
/tmp/tmp.dRhpqajJqz/mypipe
$ > '/tmp/tmp.dRhpqajJqz/mypipe' echo qwerty
$ > '/tmp/tmp.dRhpqajJqz/mypipe' echo asdf
$ > '/tmp/tmp.dRhpqajJqz/mypipe' echo Stopp
Посмотрите вывод в w1
qwerty asdf Gotcha
$
Вы также можете сделать это более автоматическим, например, как показано ниже, в котором предполагается, что существует только один временный файл с именем mypipe
,
снова запустить программу через w1
< "$dir/mypipe"./program
в w2
> $(find /tmp -name mypipe 2>/dev/null) echo 'Hello World'
> $(find /tmp -name mypipe 2>/dev/null) echo 'Stop the World'
Посмотрите вывод в w1
Hello World Gotcha
$
Демонстрационная программа C,
#include <stdio.h>
#include <string.h>
int main () {
char string[21];
while(1){
fgets(string, 20, stdin);
string[strlen(string)-1] = 0;
if(strcmp("Stop", string) == 0){
printf("Gotcha");
return 1;
}
}
}
Эта программа C записывает только после того, как прочитала 'Stop'.
Самый простой способ (с точки зрения удобочитаемости и ремонтопригодности ), на мой взгляд, состоит в том, чтобы отправить ваш awk
скрипт во временный файл, чтобы затемawk
:
awksrc=$(mktemp) || exit 1
cat << 'EOF' > "${awksrc}"
{
if ( $1 !~ /delete/ # ensure we are not trying to process deleted files
&& $4 !~ /theme.puml|config.puml/ # do not try to process our theme or custom config
&& $4 ~ /.puml/
) # only process puml files
{ printf "%s ", $4 } # only print the file name and strip newlines for spaces
}
END { print "" } # ensure we do print a newline at the end
EOF
echo "$GIT_OUTPUT" | awk -f "${awksrc}"
rm -f "${awksrc}"
Основная проблема заключается в том, что код awk
не заключен в кавычки, из-за чего оболочка заменяет в коде такие вещи, как $4
. Чтобы защитить код от оболочки, убедитесь, что документ здесь -указан в кавычках. Вы получаете цитируемый здесь документ -, заключая начальное слово-разделитель в кавычки, как в <<'AWK'
или <<"AWK"
, или экранируя его как <<\AWK
.
Вот переделка вашего сценария так, как написал бы я:
git diff-tree -r --no-commit-id --summary "$GITHUB_SHA" |
awk '
$1 !~ /^delete/ && $4 !~ /(theme|config)\.puml$/ && $4 ~ /\.puml$/ {
name[++n] = $4
}
END {
$0 = ""
for (i in name) $i = name[i]
printf "::set-output name=files::%s\n", $0
}'
Обратите внимание, что я не храню промежуточные данные в переменных. Это неэффективно, (вы можете не знать , сколько данных вам нужно хранить в переменной, )и склонны делать ошибки в кавычках и вместо этого набрасывать значения на пробелы и вызывать подстановку имен файлов. Использование вами $GIT_OUTPUT
и $AWK
без кавычек проблематично в этом отношении, а echo $GIT_OUTPUT
особенно проблематично, поскольку echo
может изменить данные, если они содержат обратную косую черту, в зависимости от конфигурации оболочки.
О цитировании :См. Когда необходимо двойное -цитирование?
Я использую стандартный pattern { action }
синтаксис сценария для создания массива name
строк, которые вы хотите разобрать. В блоке END
я создаю выходную запись $0
, которую вывожу с префиксом, который вы использовали для вывода echo
.
Вы также можете сделать это так, что оставляет немного больше места для комментариев:
git diff-tree -r --no-commit-id --summary "$GITHUB_SHA" |
awk '
$1 ~ /^delete/ {
# skip these
next
}
$4 ~ /(theme|config)\.puml$/ {
# and these...
next
}
$4 ~ /\.puml$/ {
# pick out filename (we assume no whitespace in filenames)
name[++n] = $4
}
END {
$0 = ""
for (i in name) $i = name[i]
printf "::set-output name=files::%s\n", $0
}'
Если вы хотите настаивать на том, чтобы awk
исходный код был в -документе здесь, я бы сделал это следующим образом:
awk_script=$(mktemp) || exit 1
trap 'rm -f "$awk_script"' EXIT
cat <<'AWK_CODE' >"$awk_script"
$1 !~ /^delete/ && $4 !~ /(theme|config)\.puml$/ && $4 ~ /\.puml$/ {
name[++n] = $4
}
END {
$0 = ""
for (i in name) $i = name[i]
printf "::set-output name=files::%s\n", $0
}
AWK_CODE
git diff-tree -r --no-commit-id --summary "$GITHUB_SHA" |
awk -f "$awk_script"
Т. е. сохранить сценарий awk
во временный файл, который вызывается с помощью awk -f
позже и удаляется в конце сценария (здесь с помощьюtrap
). Но для такой короткой awk
программыЯ не вижу в этом дополнительных преимуществ по сравнению с использованием скрипта в одной строке -в кавычках, как показано первым. Он беспорядочный и содержит множество дополнительных команд только для обслуживания, кроме двух центральных команд, которые необходимо выполнить.
Поместите свой код в функции, а не в переменные, что-то вроде этого (не проверено и еще есть место для улучшения):
set -x
set -e
do_awk() {
awk '
($1 !~ /delete/) && # ensure we are not trying to process deleted files
($4 !~ /theme.puml|config.puml/) && # do not try to process our theme or custom config
($4 ~ /.puml/) { # only process puml files
printf "%s ", $4 # only print the file name and strip newlines for spaces
}
END { print "" } # ensure we do print a newline at the end
' "${@:--}"
}
GIT_OUTPUT=$(git diff-tree -r --no-commit-id --summary "$GITHUB_SHA")
AWK_OUPUT=$(printf '%s\n' "$GIT_OUTPUT" | do_awk)
echo "::set-output name=files::$GIT_OUTPUT"
set +e
set +x
Я никогда не использовал GitHub Workflow, но в документации сказано, что вы можете использовать пользовательскую оболочку. Казалось бы, если сказать:
steps:
- name: process puml files
run: <your awk script here>
shell: awk -f {0}
или что-то в этом роде, вы сможете запустить необработанный awk-скрипт без махинаций с оболочкой.