2013-12-19 01:19:19 +00:00
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
# Simple jobs that never fails
|
|
|
|
# Each should be taking 30-100s and be possible to run in parallel
|
|
|
|
# I.e.: No race conditions, no logins
|
2014-02-13 23:10:49 +00:00
|
|
|
|
2019-03-17 22:07:32 +00:00
|
|
|
par_sighup() {
|
|
|
|
echo '### Test SIGHUP'
|
2017-06-18 19:15:36 +00:00
|
|
|
parallel -k -j5 sleep 15';' echo ::: {1..99} >/tmp/parallel$$ 2>&1 &
|
|
|
|
A=$!
|
2019-03-17 22:07:32 +00:00
|
|
|
sleep 29; kill -HUP $A
|
2017-06-18 19:15:36 +00:00
|
|
|
wait
|
2018-10-22 22:46:38 +00:00
|
|
|
LC_ALL=C sort /tmp/parallel$$
|
2017-06-18 19:15:36 +00:00
|
|
|
rm /tmp/parallel$$
|
|
|
|
}
|
|
|
|
|
2016-07-10 14:02:26 +00:00
|
|
|
par_race_condition1() {
|
|
|
|
echo '### Test race condition on 8 CPU (my laptop)'
|
|
|
|
seq 1 5000000 > /tmp/parallel_race_cond
|
2018-01-03 08:03:16 +00:00
|
|
|
seq 1 10 |
|
|
|
|
parallel -k "cat /tmp/parallel_race_cond | parallel --pipe --recend '' -k gzip >/dev/null; echo {}"
|
2016-07-10 14:02:26 +00:00
|
|
|
rm /tmp/parallel_race_cond
|
|
|
|
}
|
|
|
|
|
|
|
|
par_tmp_full() {
|
|
|
|
# Assume /tmp/shm is easy to fill up
|
|
|
|
export SHM=/tmp/shm/parallel
|
|
|
|
mkdir -p $SHM
|
2016-07-22 18:39:12 +00:00
|
|
|
sudo umount -l $SHM 2>/dev/null
|
2016-07-10 14:02:26 +00:00
|
|
|
sudo mount -t tmpfs -o size=10% none $SHM
|
|
|
|
|
|
|
|
echo "### Test --tmpdir running full. bug #40733 was caused by this"
|
|
|
|
stdout parallel -j1 --tmpdir $SHM cat /dev/zero ::: dummy
|
|
|
|
}
|
|
|
|
|
|
|
|
par_memory_leak() {
|
|
|
|
a_run() {
|
|
|
|
seq $1 |time -v parallel true 2>&1 |
|
|
|
|
grep 'Maximum resident' |
|
|
|
|
field 6;
|
|
|
|
}
|
|
|
|
export -f a_run
|
|
|
|
echo "### Test for memory leaks"
|
2017-03-02 21:40:46 +00:00
|
|
|
echo "Of 100 runs of 1 job at least one should be bigger than a 3000 job run"
|
|
|
|
small_max=$(seq 100 | parallel a_run 1 | jq -s max)
|
2016-07-10 14:02:26 +00:00
|
|
|
big=$(a_run 3000)
|
|
|
|
if [ $small_max -lt $big ] ; then
|
|
|
|
echo "Bad: Memleak likely."
|
|
|
|
else
|
2018-01-03 08:03:16 +00:00
|
|
|
echo "Good: No memleak detected."
|
2016-07-10 14:02:26 +00:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2018-01-03 08:03:16 +00:00
|
|
|
linebuffer_matters() {
|
|
|
|
echo "### (--linebuffer) --compress $TAG should give different output"
|
2017-01-12 22:36:15 +00:00
|
|
|
nolbfile=$(mktemp)
|
|
|
|
lbfile=$(mktemp)
|
|
|
|
controlfile=$(mktemp)
|
|
|
|
randomfile=$(mktemp)
|
|
|
|
# Random data because it does not compress well
|
|
|
|
# forcing the compress tool to spit out compressed blocks
|
2018-01-03 08:03:16 +00:00
|
|
|
perl -pe 'y/[A-Za-z]//cd; $t++ % 1000 or print "\n"' < /dev/urandom |
|
|
|
|
head -c 10000000 > $randomfile
|
|
|
|
export randomfile
|
2016-07-10 21:00:20 +00:00
|
|
|
|
2017-06-22 11:35:58 +00:00
|
|
|
testfunc() {
|
|
|
|
linebuffer="$1"
|
2018-01-03 08:03:16 +00:00
|
|
|
|
|
|
|
incompressible_ascii() {
|
|
|
|
# generate some incompressible ascii
|
|
|
|
# with lines starting with the same string
|
|
|
|
id=$1
|
|
|
|
shuf $randomfile | perl -pe 's/^/'$id' /'
|
2018-01-06 18:58:01 +00:00
|
|
|
# Sleep to give time to linebuffer-print the first part
|
|
|
|
sleep 10
|
2018-01-03 08:03:16 +00:00
|
|
|
shuf $randomfile | perl -pe 's/^/'$id' /'
|
|
|
|
echo
|
|
|
|
}
|
|
|
|
export -f incompressible_ascii
|
|
|
|
|
2018-07-08 18:45:39 +00:00
|
|
|
nowarn() {
|
|
|
|
# Ignore certain warnings
|
|
|
|
# parallel: Warning: Starting 11 processes took > 2 sec.
|
|
|
|
# parallel: Warning: Consider adjusting -j. Press CTRL-C to stop.
|
2019-01-21 02:16:59 +00:00
|
|
|
grep -v '^parallel: Warning: (Starting|Consider)' >&2
|
2018-07-08 18:45:39 +00:00
|
|
|
}
|
|
|
|
|
2018-01-03 08:03:16 +00:00
|
|
|
parallel -j0 $linebuffer --compress $TAG \
|
2018-07-08 18:45:39 +00:00
|
|
|
incompressible_ascii ::: {0..10} 2> >(nowarn) |
|
|
|
|
perl -ne '/^(\d+)\s/ and print "$1\n"' |
|
|
|
|
uniq |
|
|
|
|
sort
|
2017-06-22 11:35:58 +00:00
|
|
|
}
|
|
|
|
|
2018-01-06 18:58:01 +00:00
|
|
|
# These can run in parallel if there are enough ressources
|
|
|
|
testfunc > $nolbfile
|
|
|
|
testfunc > $controlfile
|
|
|
|
testfunc --linebuffer > $lbfile
|
2017-01-12 22:36:15 +00:00
|
|
|
wait
|
|
|
|
|
|
|
|
nolb="$(cat $nolbfile)"
|
|
|
|
control="$(cat $controlfile)"
|
|
|
|
lb="$(cat $lbfile)"
|
|
|
|
rm $nolbfile $lbfile $controlfile $randomfile
|
|
|
|
|
|
|
|
if [ "$nolb" == "$control" ] ; then
|
|
|
|
if [ "$lb" == "$nolb" ] ; then
|
|
|
|
echo "BAD: --linebuffer makes no difference"
|
|
|
|
else
|
|
|
|
echo "OK: --linebuffer makes a difference"
|
|
|
|
fi
|
2016-07-10 21:00:20 +00:00
|
|
|
else
|
2017-01-12 22:36:15 +00:00
|
|
|
echo "BAD: control and nolb are not the same"
|
2016-07-10 21:00:20 +00:00
|
|
|
fi
|
|
|
|
}
|
2018-01-03 08:03:16 +00:00
|
|
|
export -f linebuffer_matters
|
|
|
|
|
|
|
|
par_linebuffer_matters_compress_tag() {
|
|
|
|
export TAG=--tag
|
|
|
|
linebuffer_matters
|
|
|
|
}
|
2016-07-10 21:00:20 +00:00
|
|
|
|
|
|
|
par_linebuffer_matters_compress() {
|
2018-01-03 08:03:16 +00:00
|
|
|
linebuffer_matters
|
2016-07-10 21:00:20 +00:00
|
|
|
}
|
|
|
|
|
2016-12-14 07:48:27 +00:00
|
|
|
par_memfree() {
|
2018-07-08 18:45:39 +00:00
|
|
|
echo '### test memfree - it should be killed by timeout'
|
2016-12-14 07:48:27 +00:00
|
|
|
parallel --memfree 1k echo Free mem: ::: 1k
|
2018-07-08 18:45:39 +00:00
|
|
|
stdout parallel --timeout 20 --argsep II parallel --memfree 1t echo Free mem: ::: II 1t |
|
|
|
|
grep -v TERM | grep -v ps/display.c
|
|
|
|
}
|
|
|
|
|
|
|
|
par_shellquote() {
|
|
|
|
echo '### Test --shellquote in all shells'
|
|
|
|
doit() {
|
|
|
|
# Run --shellquote for ascii 1..255 in a shell
|
|
|
|
shell="$1"
|
|
|
|
"$shell" -c perl\ -e\ \'print\ pack\(\"c\*\",1..255\)\'\ \|\ parallel\ -0\ --shellquote
|
|
|
|
}
|
|
|
|
export -f doit
|
|
|
|
parallel --tag -q -k doit {} ::: ash bash csh dash fish fizsh ksh ksh93 lksh mksh posh rzsh sash sh static-sh tcsh yash zsh csh tcsh
|
2016-12-14 07:48:27 +00:00
|
|
|
}
|
2016-07-10 14:02:26 +00:00
|
|
|
|
2018-05-22 19:02:10 +00:00
|
|
|
par_test_detected_shell() {
|
|
|
|
echo '### bug #42913: Dont use $SHELL but the shell currently running'
|
|
|
|
|
|
|
|
shells="ash bash csh dash fish fizsh ksh ksh93 mksh posh rbash rush rzsh sash sh static-sh tcsh yash zsh"
|
|
|
|
test_unknown_shell() {
|
|
|
|
shell="$1"
|
|
|
|
tmp="/tmp/test_unknown_shell_$shell"
|
2019-05-05 23:06:48 +00:00
|
|
|
# Remove the file to avoid potential text-file-busy
|
|
|
|
rm -f "$tmp"
|
2018-05-22 19:02:10 +00:00
|
|
|
cp $(which "$shell") "$tmp"
|
|
|
|
chmod +x "$tmp"
|
2018-11-25 18:06:06 +00:00
|
|
|
$tmp -c 'parallel -Dinit echo ::: 1; true' |
|
2018-05-22 19:02:10 +00:00
|
|
|
grep Global::shell
|
|
|
|
rm "$tmp"
|
|
|
|
}
|
|
|
|
export -f test_unknown_shell
|
|
|
|
|
|
|
|
test_known_shell_c() {
|
|
|
|
shell="$1"
|
|
|
|
$shell -c 'parallel -Dinit echo ::: 1; true' |
|
|
|
|
grep Global::shell
|
|
|
|
}
|
|
|
|
export -f test_known_shell_c
|
|
|
|
|
|
|
|
test_known_shell_pipe() {
|
|
|
|
shell="$1"
|
|
|
|
echo 'parallel -Dinit echo ::: 1; true' |
|
|
|
|
$shell | grep Global::shell
|
|
|
|
}
|
|
|
|
export -f test_known_shell_pipe
|
|
|
|
|
2019-01-21 02:16:59 +00:00
|
|
|
stdout parallel -j2 --tag -k \
|
2018-07-08 18:45:39 +00:00
|
|
|
::: test_unknown_shell test_known_shell_c test_known_shell_pipe \
|
|
|
|
::: $shells |
|
|
|
|
grep -Ev 'parallel: Warning: (Starting .* processes took|Consider adjusting)'
|
2018-05-22 19:02:10 +00:00
|
|
|
}
|
|
|
|
|
2018-07-11 16:46:05 +00:00
|
|
|
par_linebuffer_files() {
|
|
|
|
echo 'bug #48658: --linebuffer --files'
|
|
|
|
rm -rf /tmp/par48658-*
|
|
|
|
|
|
|
|
doit() {
|
|
|
|
compress="$1"
|
|
|
|
echo "normal"
|
|
|
|
parallel --linebuffer --compress-program $compress seq ::: 100000 |
|
|
|
|
wc -l
|
|
|
|
echo "--files"
|
|
|
|
parallel --files --linebuffer --compress-program $1 seq ::: 100000 |
|
|
|
|
wc -l
|
|
|
|
echo "--results"
|
|
|
|
parallel --results /tmp/par48658-$compress --linebuffer --compress-program $compress seq ::: 100000 |
|
|
|
|
wc -l
|
|
|
|
rm -rf "/tmp/par48658-$compress"
|
|
|
|
}
|
|
|
|
export -f doit
|
2019-01-21 02:16:59 +00:00
|
|
|
parallel -j1 --tag -k doit ::: zstd pzstd clzip lz4 lzop pigz pxz gzip plzip pbzip2 lzma xz lzip bzip2 lbzip2 lrz
|
2018-07-11 16:46:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
par_no_newline_compress() {
|
|
|
|
echo 'bug #41613: --compress --line-buffer - no newline';
|
|
|
|
pipe_doit() {
|
|
|
|
tagstring="$1"
|
|
|
|
compress="$2"
|
|
|
|
echo tagstring="$tagstring" compress="$compress"
|
|
|
|
perl -e 'print "O"'|
|
|
|
|
parallel "$compress" $tagstring --pipe --line-buffer cat
|
|
|
|
echo "K"
|
|
|
|
}
|
|
|
|
export -f pipe_doit
|
|
|
|
nopipe_doit() {
|
|
|
|
tagstring="$1"
|
|
|
|
compress="$2"
|
|
|
|
echo tagstring="$tagstring" compress="$compress"
|
|
|
|
parallel "$compress" $tagstring --line-buffer echo {} O ::: -n
|
|
|
|
echo "K"
|
|
|
|
}
|
|
|
|
export -f nopipe_doit
|
2019-01-21 02:16:59 +00:00
|
|
|
parallel -j1 -qk --header : {pipe}_doit {tagstring} {compress} \
|
2018-07-11 16:46:05 +00:00
|
|
|
::: tagstring '--tagstring {#}' -k \
|
|
|
|
::: compress --compress -k \
|
|
|
|
::: pipe pipe nopipe
|
|
|
|
}
|
|
|
|
|
|
|
|
par_max_length_len_128k() {
|
|
|
|
echo "### BUG: The length for -X is not close to max (131072)"
|
|
|
|
|
|
|
|
seq 1 60000 | perl -pe 's/$/.gif/' | parallel -X echo {.} aa {}{.} {}{}d{} {}dd{}d{.} |head -n 1 |wc
|
|
|
|
seq 1 60000 | perl -pe 's/$/.gif/' | parallel -X echo a{}b{}c |head -n 1 |wc
|
|
|
|
seq 1 60000 | perl -pe 's/$/.gif/' | parallel -X echo |head -n 1 |wc
|
|
|
|
seq 1 60000 | perl -pe 's/$/.gif/' | parallel -X echo a{}b{}c {} |head -n 1 |wc
|
|
|
|
seq 1 60000 | perl -pe 's/$/.gif/' | parallel -X echo {}aa{} |head -n 1 |wc
|
|
|
|
seq 1 60000 | perl -pe 's/$/.gif/' | parallel -X echo {} aa {} |head -n 1 |wc
|
|
|
|
}
|
|
|
|
|
|
|
|
par_macron() {
|
|
|
|
print_it() {
|
|
|
|
parallel ::: "echo $1"
|
|
|
|
parallel echo ::: "$1"
|
|
|
|
parallel echo "$1" ::: "$1"
|
|
|
|
parallel echo \""$1"\" ::: "$1"
|
|
|
|
parallel -q echo ::: "$1"
|
|
|
|
parallel -q echo "$1" ::: "$1"
|
|
|
|
parallel -q echo \""$1"\" ::: "$1"
|
|
|
|
}
|
|
|
|
print_it "$(perl -e 'print "\257"')"
|
|
|
|
print_it "$(perl -e 'print "\257\256"')"
|
|
|
|
print_it "$(perl -e 'print "\257<\257<\257>\257>"')"
|
|
|
|
}
|
|
|
|
|
2019-05-05 23:06:48 +00:00
|
|
|
par_round_robin_blocks() {
|
|
|
|
echo "bug #49664: --round-robin does not complete"
|
|
|
|
seq 20000000 | parallel -j8 --block 10M --round-robin --pipe wc -c | wc -l
|
|
|
|
}
|
|
|
|
|
|
|
|
par_plus_dyn_repl() {
|
|
|
|
echo "Dynamic replacement strings defined by --plus"
|
|
|
|
|
|
|
|
unset myvar
|
|
|
|
echo ${myvar:-myval}
|
|
|
|
parallel --rpl '{:-(.+)} $_ ||= $$1' echo {:-myval} ::: "$myvar"
|
|
|
|
parallel --plus echo {:-myval} ::: "$myvar"
|
|
|
|
parallel --plus echo {2:-myval} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo {-2:-myval} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
|
|
|
|
myvar=abcAaAdef
|
|
|
|
echo ${myvar:2}
|
|
|
|
parallel --rpl '{:(\d+)} substr($_,0,$$1) = ""' echo {:2} ::: "$myvar"
|
|
|
|
parallel --plus echo {:2} ::: "$myvar"
|
|
|
|
parallel --plus echo {2:2} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo {-2:2} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
|
|
|
|
echo ${myvar:2:3}
|
|
|
|
parallel --rpl '{:(\d+?):(\d+?)} $_ = substr($_,$$1,$$2);' echo {:2:3} ::: "$myvar"
|
|
|
|
parallel --plus echo {:2:3} ::: "$myvar"
|
|
|
|
parallel --plus echo {2:2:3} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo {-2:2:3} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
|
|
|
|
echo ${#myvar}
|
|
|
|
parallel --rpl '{#} $_ = length $_;' echo {#} ::: "$myvar"
|
|
|
|
# {#} used for job number
|
|
|
|
parallel --plus echo {#} ::: "$myvar"
|
|
|
|
|
|
|
|
echo ${myvar#bc}
|
|
|
|
parallel --rpl '{#(.+?)} s/^$$1//;' echo {#bc} ::: "$myvar"
|
|
|
|
parallel --plus echo {#bc} ::: "$myvar"
|
|
|
|
parallel --plus echo {2#bc} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo {-2#bc} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
echo ${myvar#abc}
|
|
|
|
parallel --rpl '{#(.+?)} s/^$$1//;' echo {#abc} ::: "$myvar"
|
|
|
|
parallel --plus echo {#abc} ::: "$myvar"
|
|
|
|
parallel --plus echo {2#abc} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo {-2#abc} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
|
|
|
|
echo ${myvar%de}
|
|
|
|
parallel --rpl '{%(.+?)} s/$$1$//;' echo {%de} ::: "$myvar"
|
|
|
|
parallel --plus echo {%de} ::: "$myvar"
|
|
|
|
parallel --plus echo {2%de} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo {-2%de} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
echo ${myvar%def}
|
|
|
|
parallel --rpl '{%(.+?)} s/$$1$//;' echo {%def} ::: "$myvar"
|
|
|
|
parallel --plus echo {%def} ::: "$myvar"
|
|
|
|
parallel --plus echo {2%def} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo {-2%def} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
|
|
|
|
echo ${myvar/def/ghi}
|
|
|
|
parallel --rpl '{/(.+?)/(.+?)} s/$$1/$$2/;' echo {/def/ghi} ::: "$myvar"
|
|
|
|
parallel --plus echo {/def/ghi} ::: "$myvar"
|
|
|
|
parallel --plus echo {2/def/ghi} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo {-2/def/ghi} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
|
|
|
|
echo ${myvar^a}
|
|
|
|
parallel --rpl '{^(.+?)} s/^($$1)/uc($1)/e;' echo {^a} ::: "$myvar"
|
|
|
|
parallel --plus echo {^a} ::: "$myvar"
|
|
|
|
parallel --plus echo {2^a} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo {-2^a} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
echo ${myvar^^a}
|
|
|
|
parallel --rpl '{^^(.+?)} s/($$1)/uc($1)/eg;' echo {^^a} ::: "$myvar"
|
|
|
|
parallel --plus echo {^^a} ::: "$myvar"
|
|
|
|
parallel --plus echo {2^^a} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo {-2^^a} ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
|
|
|
|
myvar=AbcAaAdef
|
|
|
|
echo ${myvar,A}
|
|
|
|
parallel --rpl '{,(.+?)} s/^($$1)/lc($1)/e;' echo '{,A}' ::: "$myvar"
|
|
|
|
parallel --plus echo '{,A}' ::: "$myvar"
|
|
|
|
parallel --plus echo '{2,A}' ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo '{-2,A}' ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
echo ${myvar,,A}
|
|
|
|
parallel --rpl '{,,(.+?)} s/($$1)/lc($1)/eg;' echo '{,,A}' ::: "$myvar"
|
|
|
|
parallel --plus echo '{,,A}' ::: "$myvar"
|
|
|
|
parallel --plus echo '{2,,A}' ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
parallel --plus echo '{-2,,A}' ::: "wrong" ::: "$myvar" ::: "wrong"
|
|
|
|
}
|
|
|
|
|
|
|
|
par_keeporder_roundrobin() {
|
|
|
|
echo 'bug #50081: --keep-order --round-robin should give predictable results'
|
|
|
|
|
|
|
|
export PARALLEL="-j13 --block 1m --pipe --roundrobin"
|
|
|
|
random500m() {
|
|
|
|
< /dev/zero openssl enc -aes-128-ctr -K 1234 -iv 1234 2>/dev/null |
|
|
|
|
head -c 500m;
|
|
|
|
}
|
|
|
|
a=$(random500m | parallel -k 'echo {#} $(md5sum)' | sort)
|
|
|
|
b=$(random500m | parallel -k 'echo {#} $(md5sum)' | sort)
|
|
|
|
c=$(random500m | parallel 'echo {#} $(md5sum)' | sort)
|
|
|
|
if [ "$a" == "$b" ] ; then
|
|
|
|
# Good: -k should be == -k
|
|
|
|
if [ "$a" == "$c" ] ; then
|
|
|
|
# Bad: without -k the command should give different output
|
|
|
|
echo 'Broken: a == c'
|
|
|
|
printf "$a\n$b\n$c\n"
|
|
|
|
else
|
|
|
|
echo OK
|
|
|
|
fi
|
|
|
|
else
|
|
|
|
echo 'Broken: a <> b'
|
|
|
|
printf "$a\n$b\n$c\n"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2016-07-10 14:02:26 +00:00
|
|
|
export -f $(compgen -A function | grep par_)
|
2017-01-12 22:36:15 +00:00
|
|
|
compgen -A function | grep par_ | sort |
|
2019-01-01 13:34:04 +00:00
|
|
|
parallel --delay 0.3 -j0 --tag -k --joblog /tmp/jl-`basename $0` '{} 2>&1'
|