Use content of file as part of a Bash command - bash

I want to use the content of a file.txt as part of a bash command.
Suppose that the bash command with its options that I want to execute is:
my_command -a first value --b_long_version_option="second value" -c third_value
but the first 2 options (-a and --b_long_version_option ) are very verbose so instead of inserting directly on the command line (or bash script) I wrote them in a file.txt like this:
-a first value \
--b_long_version_option="second value"
Now I expect to call the command "my_command" with the following syntax (where "path_to/file.txt" is the path to file.txt, expressed in relative or absolute form):
my_command "$(cat path_to/file.txt)" -c third_value
This however is not the right syntax, as my command is breaking and complaining.
How should I write the new version of the command and/or the file.txt so that it is equivalent to its native bash usage?
Thanks in advance!

The quotes are preserving the newlines. Take them off.
You also don't need the cat unless you're running an old bash parser.
my_command $(<path_to/file.txt) -c third_value
You'll need to take the backslashes at the ends of lines out.
Be careful doing things like this, though. It's probably better to just put the whole command in the file, rather than just pieces of it. If you really just want arguments, maybe define them a little more carefully in an array, source the file and then apply them, like this:
in file:
myArgs=( "-a" "first value"
"--b_long_version_option=second value"
)
Note the quoting. Then run with
. file
my_command "${myArgs[#]" -c third_value
e.g.,
$: printf "[%s] " "${myArgs[#]}" -c=foo
[-a] [first value] [--b_long_version_option=second value] [-c=foo]

I haven't seen any example of what you're trying. But, there are simpler ways to achieve your goal.
Bash Alias
ll for example is a bash alias for ls -al. It usually is defined in .bash_profile or .bashrc as follows :
alias ll='ls -al'
So, what you can do is to set another alias for your shorthand command.
alias mycmd='mycommand -a first value --b_long_version_option="second value"'
then you can use it as follows :
mycmd -c third_value
Config file
You can also define a mycommand.json file or mycommand.ini file for default arguments. Then, you will need to check for config file in your software, and assign arguments from it.
Using config file is more advanced solution. You can define multiple config files. You can set default config file in /etc/mycommand/config.ini for example. When running on different directories, you should check ${cwd}/mycommand.ini to check local config file exists or not. You can even add a --config-file argument to your command.
Using alias is more convenient for small tasks, or thing that won't change much. If your command's behavior should be different in some other project, the using a config file would be a better solution.

Related

Choose placeholders for substitution inside a bash script

NOTE: The goal of this question is to find a suitable character sequence for an effective placeholder substitution in a bash script, not finding if a command is evaluated or not.
I have a skeleton named my_script.skelof a bash script in which I have to put a placeholder. I want to find a placeholder sequence that can be safely substituted (I mean that there aren't any clashes with other bash commands or other pathological substitutions, see A non-safe example for an example).
I've figured out on my own that enveloping placeholder_name within #~ and ~# seems a good solution, but I'm not sure that this solution is safe in every possible case.
A non-safe example
One can decide to use /placeholder_name/. So my_dumb_script.skel is:
#!/bin/bash
AN_INNOCENT_PATH="/a/simple/placeholder_name/path"
echo /placeholder_name/
The goal is to replace only /placeholder_name/ in the echo command. If now I use sed on the placeholder:
sed 's%/placeholder_name/%foobar%g' my_dumb_script.skel > output.bash
The output could be unexpected:
#!/bin/bash
AN_INNOCENT_PATH="/a/simplefoobarpath"
echo foobar
In this case we've obtained an unwanted substitution inside AN_INNOCENT_PATH, since it's easy to pattern-match on placeholders that contains the / character. I know that it seems very dumb, but you cannot know how people will use your code in the future (and someone could create a folder named placeholder_name).
A safe example that uses #~
In this case my_better_script.skel is the following one:
#!/bin/bash
AN_INNOCENT_PATH="/a/simple/placeholder_name/path"
echo #~placeholder_name~#
And now we can use sed:
sed 's%#~placeholder_name~#%foobar%g' my_better_script.skel > output.bash
The output now is better:
#!/bin/bash
AN_INNOCENT_PATH="/a/simple/placeholder_name/path"
echo foobar
Now everything works as intended.
You can use type to check if a command will evaluate.
$ placeholder1="testing"
$ placeholder2="test"
$ type $placeholder1
-bash: type: testing: not found
$ type $placeholder2
test is a shell builtin

How to create one output file for each file passed to a loop in bash?

I have a file that I pass to a bash command that will create an output in a loop like so:
for file in /file/list/*
do
command
done
I wish to save the output that would have gone to standard out of each loop to a text file in my working directory. Currently I am trying this:
for file in /file/list/*
do
command | tee "$file_command output.txt"
done
What I expect to see are new files created in my current directory titled file1.txt_commandoutput.txt, file2.txt_commandoutput.txt, etc. The output of the command should be saved as a different file for each file. However I get only one file created and it's called ".txt" and can't be opened by any standard software on Mac. I am new to bash scripting, so help would be much appreciated!
Thanks.
Your problem comes from the variable name you're using:
"$file_command_output.txt" looks for a variable named file_command_output (the dot cannot be in the variable name, but the alphanumerical characters and the underscore all can).
What you're looking for is "${file}_command_output.txt" to make the variable name more explicit.
You have two issues in your script.
First, the wrong parameter/variable is expanded (file_command instead of file) because it's followed by a character that can be interpreted as part of the name (the underscore, _). To fix it, enclose the parameter name in braces, like this: ${file}_command (see Shell Parameter Expansion in bash manual).
Second, even with fixed variable name expansion, the file won't be created in your working directory, because the file holds an absolute pathname (/file/list/name). To fix it, you'll have to strip the directory from the pathname. You can do that with either basename command, or even better with a modified shell parameter expansion that will strip the longest matching prefix, like this: ${file##*/} (again, see Shell Parameter Expansion, section on ${parameter##word}).
All put together, your script now looks like:
#!/bin/bash
for file in /file/list/*
do
command | tee "${file##*/}_command output.txt"
done
Also, to just save the command output to a file, without printing it in terminal, you can use a simple redirection, instead of tee, like this: command > "${file##*/}_com...".
If you are not aware of xargs, try this:
$ ls
file
$ cat > file
one
two
three
$ while read this; do touch $this; done < ./file
$ ls
file one three two

alias parameter not working

I am trying to make it easier to use scp so I learned about alias today, and I am using it like this:
alias loudie-scp="scp -i keys/aws.pem $1 ec2-user#ec2-107-20-68-112.compute-1.amazonaws.com:/home/ec2-user"
the $1 is there to specify the file i want to transfer over. However this is not working and giving me an error:
scp: /home/ec2-user: not a regular file
This does not happen when I execute this command manually passing in any file for $1.
BASH FAQ entry #80: "How can I make an alias that takes an argument?"
Unfortunately BASH aliases are kind of like find-and-replace -- they're not very powerful for the sort of task you describe. I would suggest using, instead, a script file and placing it in an executable directory; something like so:
#!/bin/bash
scp -i keys/aws.pem $1 ec2-user#ec2-107-20-68-112.compute-1.amazonaws.com:/home/ec2-user
Then, given that it has the name loudie-scp you could call it like so:
loudie-scp <parameter>
As I'm sure Ignacio's link will explain, an alias does nothing more than textually expand the alias to its value. It does not take arguments, you need to use a function for that.

Shell command to get config values

I am using the sh 3.2 in Mac Os X. I have a file test.conf
config1="Configuration 1"
config2="a lot of text"
config3=...
So I only need to get the config1= and config2= parameter. How can I set a variable, that I can do this:
> echo $variable
Configuration 1
So simple, but I am not doing it work.
the sommand you are looking for is source
source test.conf
echo $config1 #echoes Configuration 1
if you need to have config1 in variable, add
varible=$config1
At a rough guess...
export `grep 'config1=' /your/config/file`
export `grep 'config2=' /your/config/file`
But remember if you put this in a shell script file, then you'll need to eval the file rather than execute it to set the variables in the current shell instance.
You could do this:
variable=`sed -n 's/^config1=//p'`
Or if you are attempting to evaluate certain parts of your file, try something like
eval `grep ^config1= test.conf`
to have config1=Configuration 1 evaluated by the current shell. (With the example you provided, this will cause a syntax error, because the value cannot contain unquoted whitespace.)
I generally recommend beginners to stay away from backticks, but this is a situation where they are a good answer.

How to run my own programm using command in Shell?

I just learned that I could use chmod make myscript.sh executable and the run it as $ ./myscript.sh But how can I attach a custom command to it, like $ connectme [options] ?
You need to do two things:
Give the name you want to use. Either just rename it, or establish a link (hard or symbolic). Make sure the correctly named object has the right permissions.
Make sure it is in you path. But putting "." in you PATH is a bad idea (tm), so copy it to $HOME/bin, and put that in you path.
A completely different approach. Most shells support aliases. You could define one to run your script.
Note: The environment variable PATH tells the shell where to look for programs to run (unless you specify a fully qualified path like /home/jdoe/scripts/myscript.sh or ./myscript.sh), it consists of a ":" seperated list of directories to examine. You can check yours with:
$ printenv PATH
resulting for me in
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11/bin:/usr/X11R6/bin
which are the usual directories for binaries. You can add a new path element with (in /bin/sh and derivatives):
$ export PATH=$PATH:$HOME/bin
in csh and derivatives use
$ setenv PATH $PATH:$HOME/bin
either of which which will result in the shell also searching ~/bin for things to run. Then move your script into that directory (giving ta new name if you want). Check that you execute permissions for the script, and just type its name like any other command.
Fianlly, the use of a ".sh" extension to denote a shell script is for human consumption only. Unix does not care about how you name your script: it is the so-called "shebang" ("#!") on the first line of the script that the OS uses to find the interpreter.
You need to learn about arguments in BASH PROGRAMMING. Here is a good tutorial on them. Check section #4 out.
Basically, you need to use special variables $1, $2, $3 to refer to first, second and third command line arguments respectively.
Example:
$ ./mycript.sh A-Rod
With myscript.sh being:
#!/bin/bash
echo "Hello $1"
Will print:
Hello A-Rod

Resources