I have some text files $f resembling the following
function
%blah
%blah
%blah
code here
I want to append the following text before the first empty line:
%
%This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike
%3.0 Unported License. See notes at the end of this file for more information.
I tried the following:
top=$(cat ./PATH/text.txt)
top="${top//$'\n'/\\n}"
sed -i.bak 's#^$#'"$top"'\\n#' $f
where the second line (I think) preserves the new line in the text and the third line (I think) substitutes the first empty line with the text plus a new empty line.
Two problems:
1- My code appends the following text:
%n%This work is licensed under the Creative Commons
Attribution-NonCommercial-ShareAlike n%3.0 Unported License. See notes
at the end of this file for more information.\n
2- It appends it at end of the file.
Can someone please help me understand the problems with my code?
If you are using GNU sed, following would work.
Use ^$ to find the empty line and then use sed to replace/put the text that you want.
# Define your replacement text in a variable
a="%\n%This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike\n%3.0 Unported License. See notes at the end of this file for more information."
Note, $a should include those \n that will be directly interpreted by sed as newlines.
$ sed "0,/^$/s//$a/" inputfile.txt
In the above syntax, 0 represents the first occurrence.
Output:
function
%blah
%blah
%
%This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike
%3.0 Unported License. See notes at the end of this file for more information.
%blah
code here
test
You've included bash and sed tags in your question. Since I can't seem to come up with a way of doing this in sed, here's a bash-only solution. It's likely to perform the worst of all working solutions you might find.
The following works with your sample input:
$ while read -r x; do [[ -z "$x" ]] && cat boilerplate; printf '%s\n' "$x"; done < src
This will however insert the boilerplate before EVERY blank line, which is probably not what you're after. Instead, we should probably make this more than a one-liner:
#!/usr/bin/env bash
y=true
while read -r x; do
if [[ -z "$x" ]] && $y; then
cat boilerplate
y=false
fi
printf '%s\n' "$x"
done < src
Note that unlike the code in your question, this doesn't store your boilerplate in a variable, it just cats it "at the right time".
Note that this sends the combined output to stdout. If your goal is to modify the original file, you'll need to wrap this in something that moves around temporary files. (Note that sed's -i option also doesn't really edit files in place, it only hides the moving-around-temp-files from you.)
The following alternatives are probably a better idea.
A similar solution to the bash one might be achieved with better performance using awk:
awk 'NR==FNR{b=b $0 ORS;next} /^$/&&!y{printf "%s",b;y++} 1' boilerplate src
This awk solution obviously reads your boilerplate into a variable, though it's not a shell variable.
Notwithstanding non-standard platform-specific extensions, awk does not have any facility for editing files "in place" either. A portable solution using awk would still need to push temp files around.
And of course, the following old standard of ed is great to keep in your back pocket:
printf 'H\n/^$/\n-\n.r boilerplate\nw\nq\n' | ed src
In bash, of course, you could always use heretext, which might be clearer:
$ ed src <<< $'H\n/^$/\n-\n.r boilerplate\nw\nq\n'
The ed command is non-stream version of sed. Or rather, sed is the stream version of ed, which has been around since before the dinosaurs and is still going strong.
The commands we're using are separated by newlines and fed to ed's standard input. You can discard stdout if you feel the urge. The commands shown here are:
H - instruct ed to print more useful errors, if it gets any.
/^$/ - search for the first occurrence of a newline.
- - GO BACK ONE LINE. Awesome, right?
.r boilerplate - Read your boilerplate at the current line,
w - and write the file.
q - Quit.
Note that this does not keep a .bak file. You'll need to do that yourself if you really want one.
And if, as you suggested in comments, the filename you're reading is to be constructed from a variable, note that variable expansion does not happen inside format quoting ($' .. '). You can either switch quoting mechanisms mid-script:
ed "$file" <<< $'H\n/^$/\n-\n.r ./TATTOO_'"$currn"$'/top.txt\nw\nq\n'
Or you could put ed script in a variable constructed by printf
printf -v scr 'H\n/^$/\n-\n.r ./TATTOO_%s/top.txt\nw\nq\n' "$currn"
ed "$file" <<< "$scr"`
Adding the text to a variable so you can interpolate the variable is wasteful and an unnecessary complication. sed can easily read the contents of a file by itself.
sed -i.bak '1r./PATH/text.txt' "$f"
Unfortunately, this part of sed is poorly standardized, so you may have to experiment a little bit. Some dialects require a newline (perhaps, or perhaps not, preceded by a backslash) before the filename.
sed -i.bak '1r\
./PATH/text.txt' "$f"
(Notice also the double quotes around the file name. You generally always want double quotes around variables which contain file names. More here.)
Adapting the recipe from here we can extend this to apply to the first empty line instead of the first line.
sed -i.bak -e '/^$/!b' -e 'r./PATH/text.txt' -e :a -e '$!{' -e n -e ba -e } "$f"
This adds the boilerplate after the first empty line but perhaps that's acceptable. Refactoring it to replace it or add an empty line after should not be too challenging anyway. (Maybe use sed -n and instead explicitly print everything except the empty line.)
In brief terms, this skips to the end (simply prints) up until we find the first empty line. Then, we read and print the file, and go into a loop which prints the remainder of the file without returning to the beginning of the script.
sed that I think works. Uses files for the extra bit to be inserted.
b='##\n## comment piece\n##'
sed --posix -ne '
1,/^$/ {
/^$/ {
x;
/^true$/ !{
x
s/^$/true/
i\
'"$b"'
};
x;
s/^.*$//
}
}
p
' file1
with the examples using ranges of 1,/^$/, an empty first line would result in the disclaimer being printed twice. To avoid this, I've set it up to put a flag in the hold space ( x; s/^$/true/ ) that I can swap to the pattern space to check whether its the first blank. Once theres a match for blank line, i\ inserts the comment ($b) in front of the pattern space.
Thanks to ghoti for the initial plan.
Related
I'm writing a script in bash and i would like to know if there is an alternative way to write these sed commands (without using sed):
sed '1,11d;$d' "${SOTTOCARTELLA}"/file
sed '1,11!d' "${SOTTOCARTELLA}"/file
sed '1,11d' -i "${SOTTOCARTELLA}"/file1
With sed '1,11!d' "${SOTTOCARTELLA}"/file you are asking for the first 11 lines of the file;
With sed '1,11d' -i "${SOTTOCARTELLA}"/file1 you are asking for the entire file except for the first 11 lines.
If you don't want to use head, tail or other binaries as suggested, you can achieve the same options using read and some support variables.
For example, let's try sed '1,11!d' "${SOTTOCARTELLA}"/file.
You will need a start point and an end point (and of course, the file).
start=1
end=11
counter="$((start - 1))";
file="${SOTTOCARTELLA}/file"
exec 3<"${file}" ### Create file descriptor 3
while IFS= read -r line <&3; do ### Read file line by line
if [ "${counter}" -lt "${end}" ]; then ### If I'm in my "bundaries"
printf "%s\n" "${line}" ### Print the line
fi
counter="$((counter + 1))"
done
exec 3>&- ### Close file descriptor 3
Note that this piece of code can be way better (E.G. adding a control on the counter in while condition), but this is the least you will need to understand two things:
sed, head, tails, awk, etc. are born in order to avoid to rewrite over and over again same routines and, also, for performance issues; this is why everyone, including me, will be telling you to use those.
This kind of codes are useful only for portability concerns, that's why I wrote this piece of code in a posix compliant way.
I have an audacity.cfg file in which I want to script the substitution of two plugin paths. The paths were previously different, so I need to inset the updated ones. I will provide one below.
First, I want to locate this text, which begins the line in question:
FFmpegLibPath
Next, I want to replace that entire line with:
FFmpegLibPath=/Library/Application Support/audacity/libs/libavformat.55.dylib
That's it. It should not be so difficult, but it is. I have done lots of experimenting using sed and awk, but have not been able to get anything to work. While there are LOTS of examples of this online and in this forum, none of them have worked. They all produce errors relating to escape characters, as well as some random other things. I have spent hours experimenting and researching, but have not made any headway.
I realize that the slashes and spaces are likely causing issues, and I have spent considerable time attempting to solve this. I've tried all sorts of things, but as I've said, nothing works.
Does anyone have any ideas about this?
Thanks in advance for your help.
Edit:
I am running MacOS 10.10.5, and one of the things I saw in my research was using GNU sed, because some arguments do not work without it. While I am sure that would produce a better result, I cannot use it because my users would not have it. I think this is part of the reason why this is so difficult, because many of the solutions I have seen are utilizing arguments that I cannot use.
If everything other fails, you always can use the old-school ed solution. :) :)
#!/bin/bash
{
printf 'H\n'
printf '/^FFmpegLibPath[ \t=]/\n'
printf '%s\n' c 'FFmpegLibPath=/Library/Application Support/audacity/libs/libavformat.55.dylib' . w q
} | ed -s "/path/to/audacity.cfg" >/dev/null
The quotes, spaces are mandatory.
The above searching for the line starting with FFmpegLibPath and followed by space or tab or =. So it tries avoid collisions with similar prefixes like: FFmpegLibPath2.
If such collisions are not possible, the above could be simply written as:
ed -s "/path/to/audacity.cfg" >/dev/null <<'EOF'
H
/^FFmpegLibPath/
c
FFmpegLibPath=/Library/Application Support/audacity/libs/libavformat.55.dylib
.
w
q
EOF
or
printf '%s\n' H '/^FFmpegLibPath/' c 'FFmpegLibPath=/Library/Application Support/audacity/libs/libavformat.55.dylib' . w q |
ed -s "/path/to/audacity.cfg" >/dev/null
You can escape the special character (forward slash) and assign it to a variable:
REPL=$(sed 's/[\/]/\\&/g' <<< "/Library/Application Support/audacity/libs/libavformat.55.dylib")
& is sed's meta-character to represent the pattern that was matched.
sed -E "s/(FFmpegLibPath=).+/\1$REPL/" audacity.cfg
Option -E is used to support extended regular expressions
output:
etc
FFmpegLibPath=/Library/Application Support/audacity/libs/libavformat.55.dylib
etc
etc
If you preferred to maintain the updates in a separate text file:
cfg_update.txt
key_name1=value
key_name2=value
key_name3=value
# define delimiter
IFS=\=
cat cfg_update.txt | while read KEY VALUE; do
sed -i -E "s/($KEY=).+/\1$VALUE/" audacity.cfg
done
option -i is used to edit file in place
Finally, be sure to make a backup before your tests, good luck!
I'm trying to use enscript to print PDFs from Mutt, and hitting character encoding issues. One way around them seems to be to just use sed to replace the problem characters: sed -ir 's/[“”]/"/g' {input}
My test input file is this:
“very dirty”
we’re
I'm hoping to get "very dirty" and we're but instead I'm still getting
â\200\234very dirtyâ\200\235
weâ\200\231re
I found a nice little post on printing to PDFs from Mutt that I used as a starting point. I have a bash script that I point to from my .muttrc with set print_command="$HOME/.mutt/print.sh" -- the script currently reads about like this:
#!/bin/bash
input="$1" pdir="$HOME/Desktop" open_pdf=evince
# Straighten out curly quotes
sed -ir 's/[“”]/"/g' $input
sed -ir "s/[’]/'/g" $input
tmpfile="`mktemp $pdir/mutt_XXXXXXXX.pdf`"
enscript --font=Courier8 $input -2r --word-wrap --fancy-header=mutt -p - 2>/dev/null | ps2pdf - $tmpfile
$open_pdf $tmpfile >/dev/null 2>&1 &
sleep 1
rm $tmpfile
It does a fine job of creating a PDF (and works fine if you give it a file as an argument) but I can't figure out how to fix the curly quotes.
I've tried a bunch of variations on the sed line:
input=sed -r 's/[“”]/"/g' $input
$input=sed -ir "s/[’]/'/g" $input
Per the suggestion at Can I use sed to manipulate a variable in bash? I also tried input=$(sed -r 's/[“”]/"/g' <<< $input) and I get an error: "Syntax error: redirection unexpected"
But none manages to actually change $input -- what is the correct syntax to change $input with sed?
Note: I accepted an answer that resolved the question I asked, but as you can see from the comments there are a couple of other issues here. enscript is taking in a whole file as a variable, not just the text of the file. So trying to tweak the text inside the file is going to take a few extra steps. I'm still learning.
On Editing Variables In General
BashFAQ #21 is a comprehensive reference on performing search-and-replace operations in bash, including within variables, and is thus recommended reading. On this particular case:
Use the shell's native string manipulation instead; this is far higher performance than forking off a subshell, launching an external process inside it, and reading that external process's output. BashFAQ #100 covers this topic in detail, and is well worth reading.
Depending on your version of bash and configured locale, it might be possible to use a bracket expression (ie. [“”], as your original code did). However, the most portable thing is to treat “ and ” separately, which will work even without multi-byte character support available.
input='“hello ’cruel’ world”'
input=${input//'“'/'"'}
input=${input//'”'/'"'}
input=${input//'’'/"'"}
printf '%s\n' "$input"
...correctly outputs:
"hello 'cruel' world"
On Using sed
To provide a literal answer -- you almost had a working sed-based approach in your question.
input=$(sed -r 's/[“”]/"/g' <<<"$input")
...adds the missing syntactic double quotes around the parameter expansion of $input, ensuring that it's treated as a single token regardless of how it might be string-split or glob-expanded.
But All That May Not Help...
The below is mentioned because your test script is manipulating content passed on the command line; if that's not the case in production, you can probably disregard the below.
If your script is invoked as ./yourscript “hello * ’cruel’ * world”, then information about exactly what the user entered is lost before the script is started, and nothing you can do here will fix that.
This is because $1, in that scenario, will only contain “hello; ’cruel’ and world” are in their own argv locations, and the *s will have been replaced with lists of files in the current directory (each such file substituted as a separate argument) before the script was even started. Because the shell responsible for parsing the user's command line (which is not the same shell running your script!) did not recognize the quotes as valid at the time when it ran this parsing, by the time the script is running, there's nothing you can do to recover the original data.
Abstract: The way to use sed to change a variable is explored, but what you really need is a way to use and edit a file. It is covered ahead.
Sed
The (two) sed line(s) could be solved with this (note that -i is not used, it is not a file but a value):
input='“very dirty”
we’re'
sed 's/[“”]/\"/g;s/’/'\''/g' <<<"$input"
But it should be faster (for small strings) to use the internals of the shell:
input='“very dirty”
we’re'
input=${input//[“”]/\"}
input=${input//[’]/\'}
printf '%s\n' "$input"
$1
But there is an underlying problem with your script, you are trying to clean an input received from the command line. You are using $1 as the source of the string. Once somebody writes:
./script “very dirty”
we’re
That input is lost. It is broken into shell's tokens and "$1" will be “very only.
But I do not believe that is what you really have.
file
However, you are also saying that the input comes from a file. If that is the case, then read it in with:
input="$(<infile)" # not $1
sed 's/[“”]/\"/g;s/’/'\''/g' <<<"$input"
Or, if you don't mind to edit (change) the file, do this instead:
sed -i 's/[“”]/\"/g;s/’/'\''/g' infile
input="$(<infile)"
Or, if you are clear and certain that what is being given to the script is a filename, like:
./script infile
You can use:
infile="$1"
sed -i 's/[“”]/\"/g;s/’/'\''/g' "$infile"
input="$(<"$infile")"
Other comments:
Then:
Quote your variables.
Do not use the very old `…` syntax, use $(…) instead.
Do not use variables in UPPER case, those are reserved for environment variables.
And (unless you actually meant sh) use a shebang (first line) that targets bash.
The command enscript most definitively requires a file, not a variable.
Maybe you should use evince to open the PS file, there is no need of the step to make a pdf, unless you know you really need it.
I believe that is better use a file to store the output of enscript and ps2pdf.
Do not hide the errors printed by the commands until everything is working as desired, then, just call the script as:
./script infile 2>/dev/null
Or as required to make it less verbose.
Final script.
If you call the script with the name of the file that enscript is going to use, something like:
./script infile
Then, the whole script will look like this (runs both in bash or sh):
#!/usr/bin/env bash
Usage(){ echo "$0; This script require a source file"; exit 1; }
[ $# -lt 1 ] && Usage
[ ! -e $1 ] && Usage
infile="$1"
pdir="$HOME/Desktop"
open_pdf=evince
# Straighten out curly quotes
sed -i 's/[“”]/\"/g;s/’/'\''/g' "$infile"
tmpfile="$(mktemp "$pdir"/mutt_XXXXXXXX.pdf)"
outfile="${tmpfile%.*}.ps"
enscript --font=Courier10 "$infile" -2r \
--word-wrap --fancy-header=mutt -p "$outfile"
ps2pdf "$outfile" "$tmpfile"
"$open_pdf" "$tmpfile" >/dev/null 2>&1 &
sleep 5
rm "$tmpfile" "$outfile"
I went through lots of similar posts but none could be applied to mine.
I would like to search and replace using sed in some particular lines in a way that in only matches the first occurrence; lets say I have this part of the script:
processor <- read.table("../mall_all/adpcm/FULL_DB-constprop", header=TRUE, colClasses=c("reassociate"="factor", "scalarrepl"="factor", "inline"="factor", "sccp"="factor", "loop_reduce"="factor"))
processor<-processor[-c(20:40)]
processor$intensity <- processor$int_high - processor$int_low
processor$performance<- processor$perf_high - processor$perf_low
processor<-processor[-c(1:4)]
processor<-processor[,!names(processor) %in% c("constprop")]
I want to keep changing the $constprop variable in
"../mall_all/adpcm/FULL_DB-constprop"
AND
[,!names(processor) %in% c("constprop")]
in a loop that I wrote, the problem is; I want the colClasses parameteres AND the rest of the scripts remains the same while entering the loop (the loop has the compiler options like: reassociate, inline, constprop, etc)
I was wondering why my search and replace didn't work :
set -x
compilerOptionList="constprop dce inline instcombine licm loop_reduce loop_rotate loop_unroll loop_unswitch loop_unswitch mem2reg memcpyopt reassociate scalarrepl sccp simplifycfg "
stringToBeReplaced=constprop
for compilerOption in $compilerOptionList
do
echo "Using compiler option: $compilerOption"
//here you could see the sed scripts
sed -i "1,15 /FULL_DB/,/header/ s/$stringToBeReplaced/$compilerOption/" r.scr
stringToBeReplaced=$compilerOption
make
mv Rplots.pdf Rplots_adpcm_$compilerOption.pdf
echo "DONE! $compilerOption"
done
Thanks all for your time and help ;)
Amir
I'm not sure having rightly understood your need, but maybe someting like
sed -e "
1,15ba;
/FULL_DB/,/header/ba;
bb;
:a;
s/stringToBeReplaced/$compilerOption/;
:b;
" -i r.scr
could do the job.
This line is problematic
sed -i "1,15 /FULL_DB/,/header/ s/$stringToBeReplaced/$compilerOption/" r.scr
It's not a valid sed command syntax. You'll need to enclose part of it in braces like this
sed -i "1,15 { /FULL_DB/,/header/ s/$stringToBeReplaced/$compilerOption/ }" r.scr
But I think a tidier way is to use separate files for input and output of sed, i.e. change that line to
sed "1,15 s/constprop/$compilerOption/" r.scr_tmp >r.scr
You don't need the stringToBeReplaced variable. This way you always substitute "constprop", and don't have to worry that the string to be replaced appears elsewhere in the code.
r.scr_tmp would contain the same code as r.scr except that the constprop part of r.scr_tmpremains unchanged.
I want to include command-parameters-inline comments, e.g.:
sed -i.bak -r \
# comment 1
-e 'sed_commands' \
# comment 2
-e 'sed_commands' \
# comment 3
-e 'sed_commands' \
/path/to/file
The above code doesn't work. Is there a different way for embedding comments in the parameters line?
If you really want comment arguments, can try this:
ls $(
echo '-l' #for the long list
echo '-F' #show file types too
echo '-t' #sort by time
)
This will be equivalent to:
ls -l -F -t
echo is an shell built-in, so does not execute external commands, so it is fast enough. But, it is crazy anyway.
or
makeargs() { while read line; do echo ${line//#*/}; done }
ls $(makeargs <<EOF
-l # CDEWDWEls
-F #Dwfwef
EOF
)
I'd recommend using longer text blocks for your sed script, i.e.
sed -i.bak '
# comment 1
sed_commands
# comment 2
sed_commands
# comment 3
sed_commands
' /path/to/file
Unfortunately, embedded comments in sed script blocks are not universally a supported feature. The sun4 version would let you put a comment on the first line, but no where else. AIX sed either doesnt allow any comments, or uses a different char besides # for comments. Your results may vary.
I Hope this helps.
You could invoke sed multiple times instead of passing all of the arguments to one process:
sed sed_commands | # comment 1
sed sed_commands | # comment 2
sed sed_commands | # comment 3
sed sed_commands # final comment
It's obviously more wasteful, but you may decide that three extra sed processes are a fair tradeoff for readability and portability (to #shellter's point about support for comments within sed commands). Depends on your situation.
UPDATE: you'll also have to adjust if you originally intended to edit the files in place, as your -i argument implies. This approach would require a pipeline.
There isn't a way to do what you seek to do in shell plus sed. I put the comments before the sed script, like this:
# This is a remarkably straight-forward SED script
# -- When it encounters an end of here-document followed by
# the start of the next here document, it deletes both lines.
# This cuts down vastly on the number of processes which are run.
# -- It also does a substitution for XXXX, because the script which
# put the XXXX in place was quite hard enough without having to
# worry about whether things were escaped enough times or not.
cat >$tmp.3 <<EOF
/^!\$/N
/^!\\ncat <<'!'\$/d
s%version XXXX%version $SOURCEDIR/%
EOF
# This is another entertaining SED script.
# It takes the output from the shell script generated by running the
# first script through the second script and into the shell, and
# converts it back into an NMD file.
# -- It initialises the hold space with --#, which is a marker.
# -- For lines which start with the marker, it adds the pattern space
# to the hold space and exchanges the hold and pattern space. It
# then replaces a version number followed by a newline, the marker
# and a version number by the just the new version number, but
# replaces a version number followed by a newline and just the
# marker by just the version number. This replaces the old version
# number with the new one (when there is a new version number).
# The line is printed and deleted.
# -- Note that this code allows for an optional single word after the
# version number. At the moment, the only valid value is 'binary' which
# indicates that the file should not be version stamped by mknmd.
# -- On any line which does not start with the marker, the line is
# copied into the hold space, and if the original hold space
# started with the marker, the line is deleted. Otherwise, of
# course, it is printed.
cat >$tmp.2 <<'EOF'
1{
x
s/^/--#/
x
}
/^--# /{
H
x
s/\([ ]\)[0-9.][0-9.]*\n--# \([0-9.]\)/\1\2/
s/\([ ]\)[0-9.][0-9.]*\([ ][ ]*[^ ]*\)\n--# \([0-9.][0-9.]*\)/\1\3\2/
s/\([ ][0-9.][0-9.]*\)\n--# $/\1/
s/\([ ][0-9.][0-9.]*[ ][ ]*[^ ]*\)\n--# $/\1/
p
d
}
/^--#/!{
x
/^--#/d
}
EOF
There's another sed script in the file that is about 40 lines long (marked as 'entertaining'), though about half those lines are simply embedded shell script added to the output. I haven't changed the shell script containing this stuff in 13 years because (a) it works and (b) the sed scripts scare me witless. (The NMD format contains a file name and a version number separated by space and occasionally a tag word 'binary' instead of a version number, plus comment lines and blank lines.)
You don't have to understand what the script does - but commenting before the script is the best way I've found for documenting sed scripts.
No.
If you put the \ before the # it will escape the comment character and you won't have a comment anymore.
If you put the \ after the # it will be part of the comment and you won't escape the newline anymore.
A lack of inline comments is a limitation of bash that you would do better to adapt to than try and work around with some of the baroque suggestions already put forth.
Although the thread is quite old i did find it for the same question and so will others. Here's my solution for this problem:
You need comments, so that if you look at your code at a much later time you will likely get an idea of what you actually did, when you wrote the code. I am just having the same problem while writing my first rsync script, which has lots of parameters which also have side effects.
Group together your parameter which belong together by topic and put them into a variable, which gets a corresponding name. This makes it easy to identify what the parameter steer. This is your short comment. In addition you can put a comment above the variable declaration to see how you can change the behavior. This is the long version comment.
Call the application with the corresponding parameter variables.
## Options
# Remove --whole-file for delta transfer
sync_filesystem=" --one-file-system \
--recursive \
--relative \
--whole-file \ " ;
rsync \
${sync_filesystem} \
${way_more_to_come} \
"${SOURCE}" \
"${DESTIN}" \
Good overview, easy to edit and like comments in parameters. It takes more effort, but has therefore a higher quality.
I'll suggest another way that works at least in some instances:
Let's say I have the command:
foo --option1 --option2=blah --option3 option3val /tmp/bar`
I can write it this way:
options=(
--option1
--option2=blah
--option3 option3val
)
foo ${options[#]} /tmp/bar
Now let's say I want to temporarily remove the second option. I can just comment it out:
options=(
--option1
# --option2=blah
--option3 option3val
)
Note that this technique may not work when you need extensive escaping or quoting. I have run into some issues with that in the past, but unfortunately I don't recall the details at the moment :(
For most cases, though, this technique works well. If you need embedded blanks in a parameter, just enclose the string in quotes, as normal.