I have a systemd unit with spaces in an argument
ExecStart=command --argument="text text"
It seems that systemd does not recognize the double or single quotes and it splits up the argument into two arguments. Any idea how I can prevent that? I am using systemd v218 within CoreOS.
systemd only seems to recognize quotes which fully wrap arguments; i.e.
ExecStart=command "--argument=text text"
works but
ExecStart=command --argument="text text"
does not. I just ran into this issue and filed #624 about it.
This is actually surprisingly difficult to do, unfortunately. I stole this info from this answer. The only way to do it is to put your arguments in an environment file and then use them as variables as such (like in /etc/.progconfig):
ARG1=text
ARG2=text
Then import the environment file before running your command:
EnvironmentFile=/etc/.progconf
ExecStart = command $ARG1 $ARG2
As Nico suggested, you can create an EvironmentFile in which you can specify an argument with spaces.
SPACEYARG="i love spaces"
In your unit file however, you'll need to wrap that argument in curly brackets in order for the spaces to be passed properly.
EnvironmentFile=/etc/.progconf
ExecStart = command ${SPACEYARG}
I think recent versions of systemd have started accepting quotes in the middle of arguments, closer to what bash accepts. However, #Tgr's answer is still correct, and it's worth elaborating on. Quoting the entire argument, including the flag name, works here. If you do this:
ExecStart=command "--argument=text text"
Then systemd will understand --argument=text text as a single positional argument. You don't have to worry about any more splitting happening on that space. You can see the same behavior in bash:
$ echo "--silly-flag=spaces are preserved here"
--silly-flag=spaces are preserved here
Environment is a way to do it.
You can also use \s as space, so ExecStart will be:
ExecStart=command --argument="text=\stext"
ref: https://www.freedesktop.org/software/systemd/man/systemd.service.html#Command%20lines
Systemd service file supports this
Environment="TEST=one word"
Environment="TEST2=second word"
ExecStartPre=-/bin/echo start pre
ExecStartPre=/bin/echo start pre mandatory
ExecStart=/bin/echo started : ${TEST} $TEST2
ExecStartPost=-/bin/echo start post
ExecStartPost=/bin/echo start post mandatory
ExecStop=/bin/echo stop
ExecStopPost=-/bin/echo stop post
ExecStopPost=/bin/echo stop post mandatory
ExecReload=/bin/echo reload
log :
Mar 09 21:39:47 gitlab-runner-1 echo[30286]: start pre
Mar 09 21:39:47 gitlab-runner-1 echo[30288]: start pre mandatory
Mar 09 21:39:47 gitlab-runner-1 echo[30295]: started : one word second word
Mar 09 21:39:47 gitlab-runner-1 echo[30296]: start post
Mar 09 21:39:47 gitlab-runner-1 echo[30297]: start post mandatory
Mar 09 21:39:47 gitlab-runner-1 echo[30298]: stop
Mar 09 21:39:47 gitlab-runner-1 echo[30299]: stop post
Mar 09 21:39:47 gitlab-runner-1 echo[30300]: stop post mandatory
But you may actually want to set this if the app needs to read the whole string as 2 arguments, each argument between "" (not tested)
ExecStart=command "$ARG1" "$ARG2"
Try escaping the space, like so:
ExecStart=command --argument="text\ text"
(The quotes may or may not be necessary.)
Related
I understand how to reformat a date using the date command, and I am fine with that. However, I have a wrinkle in that I am struggling with - the date I want to reformat is the output of another command, so I am storing it in the variable. I am struggling with the syntax of how to specify that I want to take the output of one command, and run it through date -d, and store it in another variable. Here is what I tried:
expdate=`get_expire_date.sh`
echo $expdate
Mon 23 Mar 2022 05:05:05 PM UTC
expdval=`date -d'($expdate)'`
echo $expdval
I get today's date, not the converted expire date from the script output. If I leave the parenthesis out, of course, it treats $expdate as the literal text to translate and gives an error, whereas if I leave the single quote marks off, it uses the spaces in the date string as a delimiter and only grabs the first token.
What am I doing wrong?
First, parameter expansion doesn't occur inside single quotes. You would need to change the single quotes
expdval=`date -d'($expdate)'`
to double quotes
expdval=`date -d"($expdate)"`
Second, the parentheses create an invalid input, which results (for reasons I don't really understand) in an output of midnight of the current day. (You'll get the same result with the trivial invalid date date -d "".)
Drop the parentheses, and you'll get the same date back (because the input format matches the default output format).
$ date -d "$expdate"
Wed Mar 23 13:05:05 EDT 2022
To actually manipulate it, you'll need an explicit output format:
$ date -d "$expdate" +%Y-%m-%d
2022-03-23
or some form of date "arithmetic":
$ date -d "$expdate + 2 days"
Fri Mar 25 13:05:05 EDT 2022
I found I had to use double-quotes instead, like this (and sorry for the old way of doing things, updating to new shell syntax):
expdval=$(date -d"$(get_expire_date.sh)")
I'm trying to create a multi-line string cleanly which includes backslashes and variable substitution. Originally, I did not need backslashes and was able to achieve what I needed with this snippet:
description=$(cat <<EOF
Creation time: $(date)
Creator: $test_group_creator
Test group: $test_group
Test execution: $test_execution_id - $test_name
Adapter: $initiator1_mac_addr
http://...
EOF
)
It's not ideal as it looks a little clunky at the end and requires I break the indentation level of the code, but it's pretty clear and represents how the description will appear on a webpage later. (I am aware of the <<-EOF syntax, but we are using 4-space indents in code and that syntax requires tabs.) Here is the variable echoed out:
Creation time: Tue Jun 27 15:22:27 PDT 2017
Creator: langlorx
Test group: all
Test execution: 13 - go
Adapter: eth0
link...
However, recently, the text has been also dropped into a Wiki parser and now line-breaks are collapsed to a single space. To force a line-break mid-paragraph, the line must end in a double backslash. Knowing that bash treats backslashes specially, I knew I had to double up on the double-backslashes, but it still didn't come out right. Using this:
description=$(cat <<EOF
Creation time: $(date)\\\\
Creator: $test_group_creator\\\\
Test group: $test_group\\\\
Test execution: $test_execution_id - $test_name\\\\
Adapter: $initiator1_mac_addr\\\\
http://...
EOF
)
I find that the newlines are completely gone in the resulting variable, and last \\\\ sequence before the blank line results in a single backslash in the variable with the blank line completely gone. Without any backslashes, the resulting string comes out exactly as written above with newlines, blank lines and with the variables substituted. Once I include any backslashes at the end of a line, it triggers a different set of rules in bash for encoding the variable.
Creation time: Tue Jun 27 15:20:17 PDT 2017\\Creator: user\\Test group: all\\Test execution: 12 - go\\Adapter: eth0\link...
Is there a better way to do this in bash than trying to abuse cat and command-substitution while still having the code somewhat clean?
Store the data in the format you want it, and then just postprocess as necessary. If you want to ignore leading spaces and append \\ to each line, just do that:
embed() { sed -e 's/^ *//; s/$/\\\\/'; }
description=$(embed << eof
Creation time: $(date)
Creator: foo
Test group: bar
etc
eof
)
printf '%s\n' "$description"
will give you:
Creation time: Tue Jun 27 15:33:31 PDT 2017\\
Creator: foo\\
Test group: bar\\
etc\\
I've got a function that takes an optional date argument, and does calculations based on it. I'm trying to make sure that if that argument isn't provided, the current date/time is used as a default. I've tried this half a dozen ways, but I keep coming back to
${testDate:=$(date)}
Which works. The value of testDate is properly set, and the rest of the function works properly. However, I get an error message thrown with it:
Wed: command not found
(The date string is "Wed Mar 9 20:16:48 EST 2016" as of right now)
What am I doing wrong? Or is there a better way to do this?
Edit: To clarify, I'm emulating this, which works fine elsewhere in my script:
${menuTitle:=$"Choose from the Menu"}
How specifically is the line I posted at the top different from this one?
The typical approach in this situation is to use the null utility (builtin), :, as it both expands and ignores its arguments:
: "${testDate:=$(date)}" # double quotes not strictly required
While it won't make a difference in most scenarios, the double quotes eliminate unnecessary additional expansions, such as pathname expansion and word splitting.
Even though expansion ${testDate:=$(date)} expands to the effective value of $testDate - whether it had a previous nonempty value or was just initialized to the output from date - that value is ignored by :, resulting in the desired conditional initialization only.
Without :, with ${testDate:=$(date)} used as a statement by itself, Bash interprets whatever the expansion results in as a command, which in your case resulted in the attempt to execute the output from date as a command, which obviously failed (the first whitespace-separated token, Wed, was interpreted as the command name).
Note that the above is POSIX-compliant, so it therefore works not only in Bash, but in all major POSIX-compatible shells (bash, dash, ksh, zsh).
The problem is that this line:
${testDate:=$(date)}
not only assigns testDate a value but the variable is evaluated as well, and consequently executed as a command.
You can just do:
if [ -z "${testDate}" ]; then
testDate=$(date)
fi
Or, as a one-liner:
[ -z "${testDate}" ] && testDate=$(date)
I am trying to run a .bat file with command prompt to add time to the date.
Currently, I have this code
MOVE...\folder\^"Mytest %DATE:/=-%.csv^"
This produces
..\folder\Mytest Thu 12-06-2012.csv
I want to get
..\folder\Mytest Thu 12-06-2012 21:45.csv
Tried all kinds of things but failed miserably. Help would be greatly appreciated.
This will work:
%date:/=-% %time:~0,5%.csv
The %time% uses the current time; the :~ means "a substring of", and the 0,5 says "starting at the first character (index 0) and continuing for 5 characters", so the entire thing means "give me the first 5 characters of the output of time".
Using this at a command prompt:
C:\>echo %date:/=-% %time:~0,5%
outputs
Thu 12-06-2012 18:19
The format you're using is going to cause problems with sorting, though. My advice would be to drop the day of the week portion, and change the date output to CCYY-MM-DD, which will be much more useful when you're trying to find a specific date. You can use this:
echo %date:~10,4%-%date:~4,2%-%date:~7,2% %time:~0,5%
which outputs
2012-12-06 18:33
I'm facing the next problem in MinGW shell under windows. I have in my /etc/profile the expression:
export GIT_SSH="/c/Program Files/TortoiseGit/bin/TortoisePlink.exe"
This doesn't work when I use git fetch on the local repository. But if I do it like this (old DOS way), it works:
export GIT_SSH="/c/Progra~1/TortoiseGit/bin/TortoisePlink.exe"
My question is:
How can I make it work using spaces in the variable?
For testing purpose you can simulate something like this (any example is good):
export VAR="/c/Program Files/TortoiseGit/bin/TortoisePlink.exe"
# and try to execute like this
$VAR
Is there a solution for this (other than the previous mentioned)?
Execute it like this: "$VAR". This is one of the most significant gotchas in shell scripting because strings are always substituted literally and any contained spaces are treated as token delimiters rather than as characters of the string. Think of substituting a variable as a kind of code pasting at runtime.
What really happens when you write $VAR is that the shell tries to execute the binary /c/Program with a first argument Files/TortoiseGit/bin/TortoisePlink.exe.
I learned this the hard way by getting a strange syntax error in a big shell script for a particular input. No other languages I can think of can complain for syntax errors if the runtime input contains special characters - but that is the nature of shell scripting since command interpreters like bash and sh interpret the code line by line.
Whenever you expect a string to contain spaces and you don't want to treat it as separate tokens, enclose it in double quotes.
For reference, I solved a similar issue on osx by encapsulating the argument with escaped quotations. This may not be the best solution, but it seems to work.
alias sub="\"/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl\""
I've solved it by including a backslash to escape the space:
/Program Files becomes /Program\ Files
Example:
export GIT_SSH=/c/Program\ Files/TortoiseGit/bin/TortoisePlink.exe
With Git 2.23 (Q3 2019, eight years later), a GIT_SSH set to /c/Program\ Files/TortoiseGit/bin/TortoisePlink.exe will... work (for those still on Windows 7)!
See commit eb7c786 (16 Jul 2019) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit a5194d8, 25 Jul 2019)
mingw: support spawning programs containing spaces in their names
On some older Windows versions (e.g. Windows 7), the CreateProcessW() function does not really support spaces in its first argument, lpApplicationName.
But it supports passing NULL as lpApplicationName, which makes it figure out the application from the (possibly quoted) first argument of lpCommandLine.
Let's use that trick (if we are certain that the first argument matches
the executable's path) to support launching programs whose path contains
spaces.
This fixes git-for-windows/git issue 692
Git 2.24 (Q4 2019) adds a test:
See commit 71f4960 (01 Oct 2019) by Alexandr Miloslavskiy (SyntevoAlex).
(Merged by Junio C Hamano -- gitster -- in commit 424663d, 09 Oct 2019)
t0061: fix test for argv[0] with spaces (MINGW only)
The test was originally designed for the case where user reported that setting GIT_SSH to a .bat file with spaces in path fails on Windows: git-for-windows#692
some dirty hack for commands with spaces in variables -
for i in `k get po --all-namespaces -o wide | grep 'CrashLoop\|ImagePull' | awk '{printf " -n#%s#scale#deployment/%s",$1,$2}'`; do $(echo "kubectl ${i%-*-*} --replicas=0" | sed 's/#/ /g'); done
so here i use # instead of space forming variable and use $(echo variavle | sed 's/#/ /g') to execute lines from sed with spaces
It is hackk but it is simplier than all "corect ways" and works for me