Simple bash argument parser
Multi tool use
up vote
2
down vote
favorite
In a bash script, I need to parse arguments that have the following form:
The main arguments can be thought of as being a single argument, but I do not want to force users to quote the entire thing, so when the argument contains spaces I must be able to handle multiple arguments.
One flag may be passed as
-<flag>
where<flag>
can be an arbitrary word (without spaces)Finally, an external command, including its own options and flags may be passed. If so, this should be separated by a double dash.
For example,
my_command test
should result in
"$inp" == "test"
"$flag" == ""
"$ext_command" == ""
and
my_command this is a test -new -- sed "s|a|b|"
should result in
"$inp" == "this is a test"
"$flag" == "new"
"$ext_command" == "sed "s|a|b""
I think the following script does what I want, but since it's my first bash script, I wanted to ask whether the script is idiomatic and whether I missed any border cases.
local inp=""
local flag=""
local ext_command=""
local count="1"
local started=""
for i
do
count=$((count+1))
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
else
if [[ "$i" == -* ]];
then
flag=${i#*-}
else
if [ ! "$started" ]
then
inp="$i"
started=1
else
inp="$inp $i"
fi
fi
fi
done
bash shell
New contributor
add a comment |
up vote
2
down vote
favorite
In a bash script, I need to parse arguments that have the following form:
The main arguments can be thought of as being a single argument, but I do not want to force users to quote the entire thing, so when the argument contains spaces I must be able to handle multiple arguments.
One flag may be passed as
-<flag>
where<flag>
can be an arbitrary word (without spaces)Finally, an external command, including its own options and flags may be passed. If so, this should be separated by a double dash.
For example,
my_command test
should result in
"$inp" == "test"
"$flag" == ""
"$ext_command" == ""
and
my_command this is a test -new -- sed "s|a|b|"
should result in
"$inp" == "this is a test"
"$flag" == "new"
"$ext_command" == "sed "s|a|b""
I think the following script does what I want, but since it's my first bash script, I wanted to ask whether the script is idiomatic and whether I missed any border cases.
local inp=""
local flag=""
local ext_command=""
local count="1"
local started=""
for i
do
count=$((count+1))
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
else
if [[ "$i" == -* ]];
then
flag=${i#*-}
else
if [ ! "$started" ]
then
inp="$i"
started=1
else
inp="$inp $i"
fi
fi
fi
done
bash shell
New contributor
Is it possible to have multiple flags?
– Solomon Ucko
2 days ago
1
@SolomonUcko no, not at the moment
– Bananach
2 days ago
add a comment |
up vote
2
down vote
favorite
up vote
2
down vote
favorite
In a bash script, I need to parse arguments that have the following form:
The main arguments can be thought of as being a single argument, but I do not want to force users to quote the entire thing, so when the argument contains spaces I must be able to handle multiple arguments.
One flag may be passed as
-<flag>
where<flag>
can be an arbitrary word (without spaces)Finally, an external command, including its own options and flags may be passed. If so, this should be separated by a double dash.
For example,
my_command test
should result in
"$inp" == "test"
"$flag" == ""
"$ext_command" == ""
and
my_command this is a test -new -- sed "s|a|b|"
should result in
"$inp" == "this is a test"
"$flag" == "new"
"$ext_command" == "sed "s|a|b""
I think the following script does what I want, but since it's my first bash script, I wanted to ask whether the script is idiomatic and whether I missed any border cases.
local inp=""
local flag=""
local ext_command=""
local count="1"
local started=""
for i
do
count=$((count+1))
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
else
if [[ "$i" == -* ]];
then
flag=${i#*-}
else
if [ ! "$started" ]
then
inp="$i"
started=1
else
inp="$inp $i"
fi
fi
fi
done
bash shell
New contributor
In a bash script, I need to parse arguments that have the following form:
The main arguments can be thought of as being a single argument, but I do not want to force users to quote the entire thing, so when the argument contains spaces I must be able to handle multiple arguments.
One flag may be passed as
-<flag>
where<flag>
can be an arbitrary word (without spaces)Finally, an external command, including its own options and flags may be passed. If so, this should be separated by a double dash.
For example,
my_command test
should result in
"$inp" == "test"
"$flag" == ""
"$ext_command" == ""
and
my_command this is a test -new -- sed "s|a|b|"
should result in
"$inp" == "this is a test"
"$flag" == "new"
"$ext_command" == "sed "s|a|b""
I think the following script does what I want, but since it's my first bash script, I wanted to ask whether the script is idiomatic and whether I missed any border cases.
local inp=""
local flag=""
local ext_command=""
local count="1"
local started=""
for i
do
count=$((count+1))
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
else
if [[ "$i" == -* ]];
then
flag=${i#*-}
else
if [ ! "$started" ]
then
inp="$i"
started=1
else
inp="$inp $i"
fi
fi
fi
done
bash shell
bash shell
New contributor
New contributor
edited 2 days ago
Toby Speight
22.3k537109
22.3k537109
New contributor
asked 2 days ago
Bananach
1113
1113
New contributor
New contributor
Is it possible to have multiple flags?
– Solomon Ucko
2 days ago
1
@SolomonUcko no, not at the moment
– Bananach
2 days ago
add a comment |
Is it possible to have multiple flags?
– Solomon Ucko
2 days ago
1
@SolomonUcko no, not at the moment
– Bananach
2 days ago
Is it possible to have multiple flags?
– Solomon Ucko
2 days ago
Is it possible to have multiple flags?
– Solomon Ucko
2 days ago
1
1
@SolomonUcko no, not at the moment
– Bananach
2 days ago
@SolomonUcko no, not at the moment
– Bananach
2 days ago
add a comment |
1 Answer
1
active
oldest
votes
up vote
2
down vote
Don't go against the language
The main arguments can be thought of as being a single argument, but I do not want to force users to quote the entire thing, so when the argument contains spaces I must be able to handle multiple arguments.
If you want to a value containing spaces as a single argument,
then you and your users should double-quote it,
otherwise Bash will perform word splitting.
This is a fundamental principle in Bash,
and it's better to play along with it than to go against.
Trying to go against will get you into all kinds of trouble.
For example, what would you expect for?
my_command this is a test -new -- sed "s|a|b|"
That is, multiple spaces between words.
Those spaces will be lost,
the script will behave the same way as if there was a single space in between.
Keep in mind that users will have to quote special characters anyway.
You cannot shelter them from quoting.
It's better to learn the basic rules of word splitting and quoting early,
rather than trying to work around it with hacky solutions.
Assign arrays to arrays
This statement assigns an array to a non-array:
ext_command="${@:count}"
This way you lose the ability to expand the original value correctly quoted.
Take for example this input:
my_command test -new -- sed "s|a| |"
Notice the space in the sed
pattern.
And let's say the script uses ext_command
like this:
ls | "$ext_command"
This will not work as intended (replacing "a" with spaces), because the original arguments are not preserved correctly.
Using an array you could leave this option open, that is:
ext_command=("${@:count}")
And then later:
ls | "${ext_command[@]}"
Minor points
Instead of this:
local inp=""
You can write simply:
local inp
Instead of this:
count=$((count+1))
You can write simply:
((count++))
Instead of this:
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
else
# a long block of code ...
fi
It's more readable like this:
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
fi
# a long block of code ... but less deeply nested
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
Don't go against the language
The main arguments can be thought of as being a single argument, but I do not want to force users to quote the entire thing, so when the argument contains spaces I must be able to handle multiple arguments.
If you want to a value containing spaces as a single argument,
then you and your users should double-quote it,
otherwise Bash will perform word splitting.
This is a fundamental principle in Bash,
and it's better to play along with it than to go against.
Trying to go against will get you into all kinds of trouble.
For example, what would you expect for?
my_command this is a test -new -- sed "s|a|b|"
That is, multiple spaces between words.
Those spaces will be lost,
the script will behave the same way as if there was a single space in between.
Keep in mind that users will have to quote special characters anyway.
You cannot shelter them from quoting.
It's better to learn the basic rules of word splitting and quoting early,
rather than trying to work around it with hacky solutions.
Assign arrays to arrays
This statement assigns an array to a non-array:
ext_command="${@:count}"
This way you lose the ability to expand the original value correctly quoted.
Take for example this input:
my_command test -new -- sed "s|a| |"
Notice the space in the sed
pattern.
And let's say the script uses ext_command
like this:
ls | "$ext_command"
This will not work as intended (replacing "a" with spaces), because the original arguments are not preserved correctly.
Using an array you could leave this option open, that is:
ext_command=("${@:count}")
And then later:
ls | "${ext_command[@]}"
Minor points
Instead of this:
local inp=""
You can write simply:
local inp
Instead of this:
count=$((count+1))
You can write simply:
((count++))
Instead of this:
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
else
# a long block of code ...
fi
It's more readable like this:
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
fi
# a long block of code ... but less deeply nested
add a comment |
up vote
2
down vote
Don't go against the language
The main arguments can be thought of as being a single argument, but I do not want to force users to quote the entire thing, so when the argument contains spaces I must be able to handle multiple arguments.
If you want to a value containing spaces as a single argument,
then you and your users should double-quote it,
otherwise Bash will perform word splitting.
This is a fundamental principle in Bash,
and it's better to play along with it than to go against.
Trying to go against will get you into all kinds of trouble.
For example, what would you expect for?
my_command this is a test -new -- sed "s|a|b|"
That is, multiple spaces between words.
Those spaces will be lost,
the script will behave the same way as if there was a single space in between.
Keep in mind that users will have to quote special characters anyway.
You cannot shelter them from quoting.
It's better to learn the basic rules of word splitting and quoting early,
rather than trying to work around it with hacky solutions.
Assign arrays to arrays
This statement assigns an array to a non-array:
ext_command="${@:count}"
This way you lose the ability to expand the original value correctly quoted.
Take for example this input:
my_command test -new -- sed "s|a| |"
Notice the space in the sed
pattern.
And let's say the script uses ext_command
like this:
ls | "$ext_command"
This will not work as intended (replacing "a" with spaces), because the original arguments are not preserved correctly.
Using an array you could leave this option open, that is:
ext_command=("${@:count}")
And then later:
ls | "${ext_command[@]}"
Minor points
Instead of this:
local inp=""
You can write simply:
local inp
Instead of this:
count=$((count+1))
You can write simply:
((count++))
Instead of this:
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
else
# a long block of code ...
fi
It's more readable like this:
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
fi
# a long block of code ... but less deeply nested
add a comment |
up vote
2
down vote
up vote
2
down vote
Don't go against the language
The main arguments can be thought of as being a single argument, but I do not want to force users to quote the entire thing, so when the argument contains spaces I must be able to handle multiple arguments.
If you want to a value containing spaces as a single argument,
then you and your users should double-quote it,
otherwise Bash will perform word splitting.
This is a fundamental principle in Bash,
and it's better to play along with it than to go against.
Trying to go against will get you into all kinds of trouble.
For example, what would you expect for?
my_command this is a test -new -- sed "s|a|b|"
That is, multiple spaces between words.
Those spaces will be lost,
the script will behave the same way as if there was a single space in between.
Keep in mind that users will have to quote special characters anyway.
You cannot shelter them from quoting.
It's better to learn the basic rules of word splitting and quoting early,
rather than trying to work around it with hacky solutions.
Assign arrays to arrays
This statement assigns an array to a non-array:
ext_command="${@:count}"
This way you lose the ability to expand the original value correctly quoted.
Take for example this input:
my_command test -new -- sed "s|a| |"
Notice the space in the sed
pattern.
And let's say the script uses ext_command
like this:
ls | "$ext_command"
This will not work as intended (replacing "a" with spaces), because the original arguments are not preserved correctly.
Using an array you could leave this option open, that is:
ext_command=("${@:count}")
And then later:
ls | "${ext_command[@]}"
Minor points
Instead of this:
local inp=""
You can write simply:
local inp
Instead of this:
count=$((count+1))
You can write simply:
((count++))
Instead of this:
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
else
# a long block of code ...
fi
It's more readable like this:
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
fi
# a long block of code ... but less deeply nested
Don't go against the language
The main arguments can be thought of as being a single argument, but I do not want to force users to quote the entire thing, so when the argument contains spaces I must be able to handle multiple arguments.
If you want to a value containing spaces as a single argument,
then you and your users should double-quote it,
otherwise Bash will perform word splitting.
This is a fundamental principle in Bash,
and it's better to play along with it than to go against.
Trying to go against will get you into all kinds of trouble.
For example, what would you expect for?
my_command this is a test -new -- sed "s|a|b|"
That is, multiple spaces between words.
Those spaces will be lost,
the script will behave the same way as if there was a single space in between.
Keep in mind that users will have to quote special characters anyway.
You cannot shelter them from quoting.
It's better to learn the basic rules of word splitting and quoting early,
rather than trying to work around it with hacky solutions.
Assign arrays to arrays
This statement assigns an array to a non-array:
ext_command="${@:count}"
This way you lose the ability to expand the original value correctly quoted.
Take for example this input:
my_command test -new -- sed "s|a| |"
Notice the space in the sed
pattern.
And let's say the script uses ext_command
like this:
ls | "$ext_command"
This will not work as intended (replacing "a" with spaces), because the original arguments are not preserved correctly.
Using an array you could leave this option open, that is:
ext_command=("${@:count}")
And then later:
ls | "${ext_command[@]}"
Minor points
Instead of this:
local inp=""
You can write simply:
local inp
Instead of this:
count=$((count+1))
You can write simply:
((count++))
Instead of this:
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
else
# a long block of code ...
fi
It's more readable like this:
if [[ "$i" == '--' ]]
then
ext_command="${@:count}"
break
fi
# a long block of code ... but less deeply nested
answered yesterday
janos
96.6k12122349
96.6k12122349
add a comment |
add a comment |
Bananach is a new contributor. Be nice, and check out our Code of Conduct.
Bananach is a new contributor. Be nice, and check out our Code of Conduct.
Bananach is a new contributor. Be nice, and check out our Code of Conduct.
Bananach is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f208072%2fsimple-bash-argument-parser%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
tL,3Hq,csewrGUA,vu
Is it possible to have multiple flags?
– Solomon Ucko
2 days ago
1
@SolomonUcko no, not at the moment
– Bananach
2 days ago