Create alias in shell script using a variable - shell

I have a directory say "/dir". Inside this directory I have files with the name arg1_config.tcl, arg2_config.tcl, arg3_config.tcl. There might be more files going forward with the same extension. I am trying to dynamically generate aliases for arg1, arg2 and arg3 in this case and below is my code for the same.
foreach i (`ls <path>/dir/*.tcl`)
set app = `echo $i | sed -e 's/.*\///g' | sed 's/_config.tcl//g'`
echo "app is $app"
alias $app 'run -app $app' # run is an internal script that takes arg1/2/3 as a switch
echo "alias $app 'run -app $app'"
end
When I source this file it prints
app is arg1
alias arg1 'run -app arg1'
app is arg2
alias arg2 'run -app arg2'
app is arg3
alias arg3 'run -app arg3'
However when I run which arg3 it says aliased to run -app $app and app value somehow is always the last value after exiting the foreach loop i.e arg3 in this case. I am not able to create aliases like the print messages above, i.e:
alias arg1 'run -app arg1'
alias arg2 'run -app arg2'
alias arg3 'run -app arg3'

I know it's a bash faq, but the ParsingLs guidelines apply here too. Don't parse the output of ls. Just don't. You have globs in csh too.
foreach i (`ls <path>/dir/*.tcl`)
should simply be
foreach i ( <path>/dir/*.tcl )
That said, the problem you're asking about is as Glenn suggested in comments. Single and double quotes behave in csh much the same way as they do in sh/bash: single quotes block variable expansion. Your alias is not actually setting an alias, it's setting something which when run will try to expand the variable at the time you run it. Try using double quotes and see if that gets you the behaviour you expect.
As an alternate strategy to shell aliases, consider linking or symlinking the script to multiple names on your path, then switching on $0 instead. It'll require less hacking on shells, which will be especially noticeable when someone decides to try out a different shell and forgets that these "commands" are really just shell aliases. :)

Related

Combining variables in Bash to form a command sent to AppleScript using the osascript command

I'm trying to make this script work. It's a Bash script that is meant to take some variables, put them together and use the result to send an AppleScript command. Manually pasting the string echoed from the variable to_osa behind osascript -e to the terminal works as I want and expect it to. But when I try to combine the command osascript -e and the string to_osa, it does not work. How can I make this work?
the_url="\"http://stackoverflow.com/questions/1521462/looping-through-the-content-of-a-file-in-bash\""
the_script='tell application "Safari" to set the URL of the front document to '
delimiter="'"
to_osa=${delimiter}${the_script}${the_url}${delimiter}
echo ${to_osa}
osascript -e ${to_osa}
In addition to working manually the script also works when I write the desired command to a script and then execute it:
echo "osascript -e" $to_osa > ~/Desktop/outputfile.sh
sh ~/Desktop/outputfile.sh
String mashing executable code is error prone and evil, and there's absolutely no need for it here. It's trivial to pass arguments to an AppleScript by defining an explicit 'run' handler:
on run argv -- argv is a list of strings
-- do stuff here
end run
which you then invoke like so:
osascript -e /path/to/script arg1 arg2 ...
BTW, if your script requires a fixed number of args, you also write it like this:
on run {arg1, arg2, ...} -- each arg is a string
-- do stuff here
end run
...
Going further, you can even make the AppleScript directly executable as you would any other shell script. First, add a hashbang as follows:
#!/usr/bin/osascript
on run argv
-- do stuff here
end run
then save it in uncompiled plain text format and run chmod +x /path/to/myscript to make the file executable. You can then execute it from the shell as follows:
/path/to/myscript arg1 arg2 ...
Or, if you don't want to specify the full path every time, put the file in /usr/local/bin or some other directory that's on your shell's PATH:
myscript arg1 arg2 ...
...
So here's how you should be writing your original script:
#!/bin/sh
the_url="http://stackoverflow.com/questions/1521462/looping-through-the-content-of-a-file-in-bash"
osascript -e 'on run {theURL}' -e 'tell application "Safari" to set URL of document 1 to theURL' -e 'end run' $the_url
Quick, simple, and very robust.
--
p.s. If you'd rather open a URL in a new window rather than an existing one, see the manpage for OS X's open tool.
As a general rule, don't put double-quotes in the variable, put them around the variable. In this case it's more complicated, since you have some double-quotes for bash-level quoting, and some for AppleScript-level quoting; in this case, the AppleScript-level quotes go in the variable, the bash-level quotes go around the variable:
the_url="\"http://stackoverflow.com/questions/1521462/looping-through-the-content-of-a-file-in-bash\""
the_script='tell application "Safari" to set the URL of the front document to '
osascript -e "${the_script}${the_url}"
BTW, using echo to check things like this is highly misleading. echo is telling you what's in the variable, not what'll be executed when you reference the variable on a command line. The biggest difference is that echo prints its arguments after they've been through bash parsing (quote and escape removal, etc), but when you say "Manually pasting the string ... works" you're saying it's what you want before parsing. If the quotes are there in the echoed string, that means bash didn't recognize them as quotes and remove them. Compare:
string='"quoted string"'
echo $string # prints the string with double-quotes around it because bash doesnt't recognize them in a variable
echo "quoted string" # prints *without* quotes because bash recognizes and removes them

zsh run a command stored in a variable?

In a shell script (in .zshrc) I am trying to execute a command that is stored as a string in another variable. Various sources on the web say this is possible, but I'm not getting the behavior i expect. Maybe it's the ~ at the beginning of the command, or maybe it's the use of sudo, I'm not sure. Any ideas? Thanks
function update_install()
{
# builds up a command as a string...
local install_cmd="$(make_install_command $#)"
# At this point the command as a string looks like: "sudo ~some_server/bin/do_install arg1 arg2"
print "----------------------------------------------------------------------------"
print "Will update install"
print "With command: ${install_cmd}"
print "----------------------------------------------------------------------------"
echo "trying backticks"
`${install_cmd}`
echo "Trying \$()"
$(${install_cmd})
echo "Trying \$="
$=install_cmd
}
Output:
Will update install
With command: sudo ~some_server/bin/do_install arg1 arg2
trying backticks
update_install:9: no such file or directory: sudo ~some_server/bin/do_install arg1 arg2
Trying $()
update_install:11: no such file or directory: sudo ~some_server/bin/do_install arg1 arg2
Trying $=
sudo ~some_server/bin/do_install arg1 arg2: command not found
Use eval:
eval ${install_cmd}
As explained in §3.1 "Why does $var where var="foo bar" not do what I expect?" of the Z-Shell FAQ, you can use the shwordsplit shell option to tell zsh that you want it to split variables up by spaces and treat them as multiple words. That same page also discusses alternatives that you might want to consider.
I believe you have two problems here - the first is that your install_cmd is being interpreted as a single string, instead of a command (sudo) with 3 arguments.
Your final attempt $=install_cmd actually does solve that problem correctly (though I'd write it as ${=install_cmd} instead), but then you hit your second problem: ~some_server/bin/do_install is not a known command. This is because sudo doesn't interpret the ~ like you intend, for safety reasons; it would need to evaluate its arguments using the shell (or do some special-casing for ~, which is really none of sudo's business), which opens up a whole can of worms that, understandably, sudo does its best to avoid.
That's also why it worked to do eval ${install_cmd} - because that's literally treating the whole string as a thing to be evaluated, possibly containing multiple commands (e.g. if install_cmd contained echo foo; sudo rm -rf / it would be happy to wipe your system).
You have to be the one to decide whether you want install_cmd to allow full shell semantics, including variable interpolation, path expansion, multiple commands, etc. or whether it should just expand the words out and run them as a single command.
Previous answers are correct, what worked for me was the below command in zsh
totalin=$(eval echo testing |wc -l)
echo "Total: $totalin"

running a script from another script

I have a script located at /tmp/"My Batch Script Files"/Processing
How would I run this from the home directory /tmp/ in another script
If it is executable:
"/tmp/My Batch Script Files/Processing" arg1 arg2 ...
If it is not exectuable, either make it executable or:
bash "/tmp/My Batch Script Files/Processing" arg1 arg2 ...
You can place the double quotes around the whole path name as I did, or around the component containing the spaces, or almost any other location you choose in between. With just two double quotes, this as close together as you can place them:
/tmp/My" Batch Script "Files/Processing arg1 arg2 ...
or use backslashes if you prefer (I don't recommend them, though):
/tmp/My\ Batch\ Script\ Files/Processing arg1 arg2 ...
On the whole, though, I'd go with the first suggestion, or use:
proc_script="/tmp/My Batch Script Files/Processing"
"$proc_script" arg1 arg2 ...
This is particularly attractive if you generate the file name while the outer script is running.

Can I pipe between commands, and run the programs from different directories?

I have a particular command, that reacts differently depending on the content of the current working directory.
I now wish to pipe this program back to itself, back have the call happen in different directories.
In "pseudo-bash", I want
command arg1 | cd /other dir | command arg2
I personally use bash, but if it helps to use a different shell, I'm open to suggestions. :)
I realize there is a very easy workaround with a temporary file or named pipe, but I want to know if there's a way to do this in one command.
command arg1 | ( cd /other_dir ; command arg2 )
(…) executes a command in a subshell. cd is a shell builtin command, not a 'real process'. ( cd X ; command ) will start a new sub-shell, cd into X, then run command. command is running as a process, but in a different directory.
Going forward it's better to have commands that can take a directory as an argument (and if not defined, default to the current working directory). Then you could have the simple solution of command arg1 | command --dir=/other_dir arg2
How about using a subshell, something like this:
command arg1 | (cd /other/dir; command arg2)
Pipes don't work that way. They are simply a way to pass data streams (not context) from one command to another. If you need a command in a pipeline to run in a different context from the others, you'll just have to change the directory in that subshell, as #JoachimPileborg pointed out.
The canonical way to solve this in *nix shells is to instead pass the relevant directory as a parameter to the script. Your command sequence would then be:
command arg1 .
command arg2 /other/dir

How do I defer backticks (or $()) in a possibly-quoted variable in Bash?

I am trying to get Bash to execute the following minimized example properly:
# Runs a command, possibly quoted (i.e. single argument)
function run()
{
$*
}
run ls # works fine
run "ls" # also works
run "ls `pwd`" # also works, but pwd is eagerly evaluated (I want it to evaluate inside run)
run "ls \\\`pwd\\\`" # doesn't work (tried other variants as well)
To summarize, I am trying to get the ability of having commands in quoted strings (or not), and not having any of the command, including nested shell commands through backticks, calculated values, etc., evaluated before run() is called. Is this possible? How can I achieve this?
Well the way to do this sort of thing is to use the eval function associated with an escaped '$' :
function run()
{
eval $*
}
my_command="ls \$(pwd)"
Escaping '$' as '\$' ensure that my_command will be set to "ls $(pwd)" with no substitution. Then eval will provide the substitution ^^
then
run $my_command
cd ..
run $my_command
prove that you get your functionnality !
my2c

Resources