I'm making a custom zsh theme for when you open up your terminal. I'm not that new to using oh-my-zsh but I am new to zsh documentation.
Here's what I'm having problems with:
wolf='
.-'''''-.
.' `.
: :
: :
: _/| :
: =/_/ :
`._/ | .'
( / ,|...-'
\_/^\/||__
_/~ `""~`"` \_
__/ -'/ `-._ `\_\__
/ /-'` `\ \ \-.\\
'
print -P $wolf
When I try it in terminal I get two errors:
/Users/User/.oh-my-zsh/themes/wolf.zsh-theme:4: no such file or directory: . .\n : :\n : :\n : _/| :\n : =/_/ :\n._/ |
/Users/User/.oh-my-zsh/themes/wolf.zsh-theme:31: parse error near `\n
I'm guessing that these errors have to do with the apostrophes in the ASCII art.
The issue here (as jdv noted in his comment) is quoting.
In zsh:
it is not possible to use single-quotes (') withing single-quoted text. Any text within two single-quotes is taken verbatim, no additional quoting is possible. Single quotes can only be quoted with a preceding backslash or inside double quotes.
Within double-quotes (") backticks (`), dollar signs ($) and backslashes (\) are treated specially, and need to be quoted with a preceding backslash.
As quite a lot of these characters appear in your ASCII-art, you have to make sure, that they are all quoted properly.
Additionally, you use the builtin print, which by default also interprets \ as escape character.
There are two basic solutions:
Fully quote the string and use print -r or echo -E to print the text. In both cases the parameter disables escape sequence handling.
The easiest way to achieve full quoting is probably to keep the surrounding single-quotes and replace any ' within with '\''. At every occurrence this closes the previous single-quoted text, adds a quoted single-quote and starts a new single-quoted text. Any other special character is then quoted within single quotes.
wolf='
.-'\'''\'''\'''\'''\''-.
.'\'' `.
: :
: :
: _/| :
: =/_/ :
`._/ | .'\''
( / ,|...-'\''
\_/^\/||__
_/~ `""~`"` \_
__/ -'\''/ `-._ `\_\__
/ /-'\''` `\ \ \-.\\
'
print -r $wolf
Use a here-document with cat to avoid the whole quoting issue:
cat <<'END'
.-'''''-.
.' `.
: :
: :
: _/| :
: =/_/ :
`._/ | .'
( / ,|...-'
\_/^\/||__
_/~ `""~`"` \_
__/ -'/ `-._ `\_\__
/ /-'` `\ \ \-.\\
END
Note the single quotes around 'END'. This needs to be done in order to disable parameter substitution ($FOO) and command substitution ($(command) or `command`) inside the here-document.
Related
I wrote this function in zsh
function test() {
test="a\tb\tc"
while IFS=$'\t' read -r user host key; do
echo "user: $user"
echo "host: $host"
echo "key: $key"
done <<< "$test"
}
The output was:
user: a b c
host:
key:
if instead of
... IFS=$'\t' read -r ...
I change it to
... IFS='\t' read -r ...
the output is
user: a
host:
key: b c
Just what is going on?
I would like to read the tab separated line and set my variables accordingly.
Changing the double-quotes to $'...' (single quotes preceded by a $) could rescue for the variable $test:
test=$'a\tb\tc'
Here is the zsh manual for QUOTING (double-quoting and $'...'):
QUOTING
...
A string enclosed between $' and ' is processed the same way as the string arguments of the print builtin
...
Inside double quotes (""), parameter and command substitution occur, and \ quotes the characters \, `, ", $, and the first character of $histchars (default !).
--- zshmisc(1), QUOTING
For example:
"\$" -> $, "\!" -> ! etc.
"\t" -> \t (zsh does not recognize as tab this case), "\a" -> \a etc.
It does not treat the escape sequence \t as tab when it is used inside double quotes, so "a\tb\tc" does not mean "atabbtabc". (But things are a little more complicated: builtin echo recognizes the escape sequence \t.)
(1) ... IFS=$'\t' read -r ... (the original form)
Because expanding "$test" dose not have any tab characters, so read assigns the whole line to $user:
user: a b c
host:
key:
(But echo recognizes the escape sequence \t as the tab.)
(2) ... IFS='\t' read -r ...
Again, expanding "$test" does not have any tab characters, so read separate the field by \ and t according $IFS.
a\t\b\tc splits into a (to $user), \(separator), `` (empty to $host), t(separator), and the rest of the line (b\tc to $key):
user: a
host:
key: b c
(But again, echo recognizes the escape sequence \t as the tab.)
Here is the code changed from test="..." to test=$'...':
function test() {
test=$'a\tb\tc'
while IFS=$'\t' read -r user host key; do
echo "user: $user"
echo "host: $host"
echo "key: $key"
done <<< "$test"
}
test
The output is:
user: a
host: b
key: c
PS: it is worth reading POSIX's Quoting specification, which is simpler than zsh's (https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02)
I found this sudo wrapper from Zsh mailing list:
alias sudo='noglob do_sudo '
function do_sudo
{
integer glob=1
local -a run
run=( command sudo )
if [[ $# -gt 1 && $1 = -u ]]; then
run+=($1 $2)
shift ; shift
fi
(($# == 0)) && 1=/bin/zsh
while (($#)); do
case "$1" in
command|exec|-) shift; break ;;
nocorrect) shift ;;
noglob) glob=0; shift ;;
*) break ;;
esac
done
if ((glob)); then
PATH="/sbin:/usr/sbin:/usr/local/sbin:$PATH" $run $~==*
else
PATH="/sbin:/usr/sbin:/usr/local/sbin:$PATH" $run $==*
fi
}
It uses $~==* to expand the file patterns. What is this kind of expansion called? Where is it documented? (really have got no idea how to search for this, 'dollar tilde expansion', 'filename expansion', 'tilde expansion' all just gave me some irrelevant results...)
And I noticed that actually $~var also works, e.g.
$ touch foo bar
$ t1='fo*'
$ echo $~t1
foo
$ t2=('fo*' 'ba*')
$ echo $~t2
foo bar
Is it any different from $~==t1? Btw it seems there can be an arbitrary number of =s between $ and variable name, $~=t1 $=~t1 $~=====t1 all look the same.
Thanks to #chepner for the hint. Here's what I found in zshexpn manual:
${=spec}
Perform word splitting using the rules for SH_WORD_SPLIT during the evaluation of spec, but regardless of whether the parameter appears in double quotes; if the `=' is
doubled, turn it off. This forces parameter expansions to be split into separate words before substitution, using IFS as a delimiter. This is done by default in most
other shells.
Note that splitting is applied to word in the assignment forms of spec before the assignment to name is performed. This affects the result of array assignments with the A
flag.
${~spec}
Turn on the GLOB_SUBST option for the evaluation of spec; if the `~' is doubled, turn it off. When this option is set, the string resulting from the expansion will be in‐
terpreted as a pattern anywhere that is possible, such as in filename expansion and filename generation and pattern-matching contexts like the right hand side of the `='
and `!=' operators in conditions.
In nested substitutions, note that the effect of the ~ applies to the result of the current level of substitution. A surrounding pattern operation on the result may can‐
cel it. Hence, for example, if the parameter foo is set to *, ${~foo//\*/*.c} is substituted by the pattern *.c, which may be expanded by filename generation, but
${${~foo}//\*/*.c} substitutes to the string *.c, which will not be further expanded.
So ~ enables globbing which substitutes patterns (specs) with file names. == makes sure that word splitting is disabled for strings in quotes.
For example:
$ touch foo bar
$ spec='fo* ba*'
$ echo $~spec
zsh: no matches found: fo* ba*
$ echo $~=spec
foo bar
$ echo $~==spec
zsh: no matches found: fo* ba*
$ function test1() { printf '1=%s 2=%s\n' $1 $2; }
$ test1 $spec
1=fo* ba* 2=
$ test1 $~spec
zsh: no matches found: fo* ba*
$ test1 $~=spec
1=foo 2=bar
$ test1 $~==spec
zsh: no matches found: fo* ba*
$ test1 $=spec
1=fo* 2=ba*
I have several shell commands that I want to run in in R.
I have tried system() but I have not find out how to do the right escaping even using shQuote.
# works OK
system('ls -a -l')
but how I execute a command like perl -e 'print "test\n"' or curl --data-urlencode query#biomart.xml http://biomart.org/biomart/martservice/results inside R?
update:
In the case of commands like the perl example I do not know how to do the escaping of quotes as it needs to be quoted as string but already use both types of quotes.
In the case of curl, the problem seems to be in the RESTful call to pass the xml with the # that works in the shell but not in the system() call
dat <-system('curl --data-urlencode query#biomart.xml http://biomart.org/biomart/martservice/results', intern=F)
Warning: Couldn't read data from file "query#biomart.xml", this makes an empty
Warning: POST.
The file is biomart.xml not query#biomart.xml
** Update2**
The xml file I am using for test is:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Query>
<Query virtualSchemaName = "default" formatter = "TSV" header = "0" uniqueRows = "0" count = "" datasetConfigVersion = "0.6" >
<Dataset name = "hsapiens_gene_ensembl" interface = "default" >
<Filter name = "hgnc_symbol" value = "LDLR"/>
<Attribute name = "external_gene_id" />
</Dataset>
</Query>
Strings in R may be enclosed in either single (') or double (") quotes.
If you want to execute a command with both single and double quotes, such as:
perl -e 'print "test\n"'
then it is of little consequence which you choose for your R string - since one pair needs to be escaped either way.
Let's say you choose single quotes:
system('')
Then we need to escape the single quotes in the same way as for the newline character, with the escape character, \:
command <- 'perl -e \'print "test\n"\''
system(command)
It is also possible to encode Unicode characters in this way with \Unnnnnnnn or \unnnn. Alternatively with octal (\nnn), or hex (\xnnn).
Thus:
atSymbol <- '\u0040' # '\x040' '\100'
If the # in your curl command is causing the problem, encoding it like this should fix it.
In this example, in addition to escaping ', I had to escape \\ (with \\\\), \., \G, \K, but not \n
⌄ ⌄⌄ ⌄ ⌄ ⌄ ⌄ ⌄ ⌄
perl -0777 -pi -e ' s{ \\usage.*?\n\.\.\.\n} { ($r = $&) =~ s/\n//g; $r =~ s/\G.{0,79}(,|.$)\K/\n/g; $r }gse' filename.txt
system('perl -0777 -pi -e \' s{\\\\usage.*?\n\\.\\.\\.\n}{ ($r = $&) =~ s/\n//g; $r =~ s/\\G.{0,79}(,|.$)\\K/\n/g; $r }gse\' filename.txt')
i want to set the value of inputLineNumber to 20. I tried checking if no value is given by user by [[-z "$inputLineNumber"]] and then setting the value by inputLineNumber=20. The code gives this message ./t.sh: [-z: not found as message on the console. How to resolve this? Here's my full script as well.
#!/bin/sh
cat /dev/null>copy.txt
echo "Please enter the sentence you want to search:"
read "inputVar"
echo "Please enter the name of the file in which you want to search:"
read "inputFileName"
echo "Please enter the number of lines you want to copy:"
read "inputLineNumber"
[[-z "$inputLineNumber"]] || inputLineNumber=20
for N in `grep -n $inputVar $inputFileName | cut -d ":" -f1`
do
LIMIT=`expr $N + $inputLineNumber`
sed -n $N,${LIMIT}p $inputFileName >> copy.txt
echo "-----------------------" >> copy.txt
done
cat copy.txt
Changed the script after suggestion from #Kevin. Now the error message ./t.sh: syntax error at line 11: `$' unexpected
#!/bin/sh
truncate copy.txt
echo "Please enter the sentence you want to search:"
read inputVar
echo "Please enter the name of the file in which you want to search:"
read inputFileName
echo Please enter the number of lines you want to copy:
read inputLineNumber
[ -z "$inputLineNumber" ] || inputLineNumber=20
for N in $(grep -n $inputVar $inputFileName | cut -d ":" -f1)
do
LIMIT=$((N+inputLineNumber))
sed -n $N,${LIMIT}p $inputFileName >> copy.txt
echo "-----------------------" >> copy.txt
done
cat copy.txt
Try changing this line from:
[[-z "$inputLineNumber"]] || inputLineNumber=20
To this:
if [[ -z "$inputLineNumber" ]]; then
inputLineNumber=20
fi
Hope this helps.
Where to start...
You are running as /bin/sh but trying to use [[. [[ is a bash command that sh does not recognize. Either change the shebang to /bin/bash (preferred) or use [ instead.
You do not have a space between [[-z. That causes bash to read it as a command named [[-z, which clearly doesn't exist. You need [[ -z $inputLineNumber ]] (note the space at the end too). Quoting within [[ doesn't matter, but if you change to [ (see above), you will need to keep the quotes.
Your code says [[-z but your error says [-z. Pick one.
Use $(...) instead of `...`. The backticks are deprecated, and $() handles quoting appropriately.
You don't need to cat /dev/null >copy.txt, certainly not twice without writing to it in-between. Use truncate copy.txt or just plain >copy.txt.
You seem to have inconsistent quoting. Quote or escape (\x) anything with special characters (~, `, !, #, $, &, *, ^, (), [], \, <, >, ?, ', ", ;) or whitespace and any variable that could have whitespace. You don't need to quote string literals with no special characters (e.g. ":").
Instead of LIMIT=`expr...`, use limit=$((N+inputLineNumber)).
The following shell script takes a list of arguments, turns Unix paths into WINE/Windows paths and invokes the given executable under WINE.
#! /bin/sh
if [ "${1+set}" != "set" ]
then
echo "Usage; winewrap EXEC [ARGS...]"
exit 1
fi
EXEC="$1"
shift
ARGS=""
for p in "$#";
do
if [ -e "$p" ]
then
p=$(winepath -w $p)
fi
ARGS="$ARGS '$p'"
done
CMD="wine '$EXEC' $ARGS"
echo $CMD
$CMD
However, there's something wrong with the quotation of command-line arguments.
$ winewrap '/home/chris/.wine/drive_c/Program Files/Microsoft Research/Z3-1.3.6/bin/z3.exe' -smt /tmp/smtlib3cee8b.smt
Executing: wine '/home/chris/.wine/drive_c/Program Files/Microsoft Research/Z3-1.3.6/bin/z3.exe' '-smt' 'Z: mp\smtlib3cee8b.smt'
wine: cannot find ''/home/chris/.wine/drive_c/Program'
Note that:
The path to the executable is being chopped off at the first space, even though it is single-quoted.
The literal "\t" in the last path is being transformed into a tab character.
Obviously, the quotations aren't being parsed the way I intended by the shell. How can I avoid these errors?
EDIT: The "\t" is being expanded through two levels of indirection: first, "$p" (and/or "$ARGS") is being expanded into Z:\tmp\smtlib3cee8b.smt; then, \t is being expanded into the tab character. This is (seemingly) equivalent to
Y='y\ty'
Z="z${Y}z"
echo $Z
which yields
zy\tyz
and not
zy yz
UPDATE: eval "$CMD" does the trick. The "\t" problem seems to be echo's fault: "If the first operand is -n, or if any of the operands contain a backslash ( '\' ) character, the results are implementation-defined." (POSIX specification of echo)
bash’s arrays are unportable but the only sane way to handle argument lists in shell
The number of arguments is in ${#}
Bad stuff will happen with your script if there are filenames starting with a dash in the current directory
If the last line of your script just runs a program, and there are no traps on exit, you should exec it
With that in mind
#! /bin/bash
# push ARRAY arg1 arg2 ...
# adds arg1, arg2, ... to the end of ARRAY
function push() {
local ARRAY_NAME="${1}"
shift
for ARG in "${#}"; do
eval "${ARRAY_NAME}[\${#${ARRAY_NAME}[#]}]=\${ARG}"
done
}
PROG="$(basename -- "${0}")"
if (( ${#} < 1 )); then
# Error messages should state the program name and go to stderr
echo "${PROG}: Usage: winewrap EXEC [ARGS...]" 1>&2
exit 1
fi
EXEC=("${1}")
shift
for p in "${#}"; do
if [ -e "${p}" ]; then
p="$(winepath -w -- "${p}")"
fi
push EXEC "${p}"
done
exec "${EXEC[#]}"
I you do want to have the assignment to CMD you should use
eval $CMD
instead of just $CMD in the last line of your script. This should solve your problem with spaces in the paths, I don't know what to do about the "\t" problem.
replace the last line from $CMD to just
wine '$EXEC' $ARGS
You'll note that the error is ''/home/chris/.wine/drive_c/Program' and not '/home/chris/.wine/drive_c/Program'
The single quotes are not being interpolated properly, and the string is being split by spaces.
You can try preceeding the spaces with \ like so:
/home/chris/.wine/drive_c/Program Files/Microsoft\ Research/Z3-1.3.6/bin/z3.exe
You can also do the same with your \t problem - replace it with \\t.