Command works in terminal but not as alias in profile.d - linux

I have a problem regarding an alias file in /etc/profile.d/. This isn't anything important. I'm just interested why it isn't working as expected.
So basically I have the file 00-alias.sh at the path mentioned above and I wanted to make a shortcut which reads a specific line of a file. So this is my code:
alias lnn='sed -n "${1}p" < "${2}"'
With that code I should be able to perform a command like
$ lnn 4 test.txt
However, this doesn't work. I simply get the error
-bash: : No such file or directory
Now I thought, ok, maybe relative paths aren't working because the file is located at the path /etc/profile.d/00-alias.sh
So I went ahead and made a new alias like
alias pwd2='echo $(pwd)'
Then updated the profile.d with
source /etc/profile.d/00-alias.sh
And just tried pwd2 but that echoed the path I was currently in. So in theory the file can be found with the command I wrote. I still tried to pass the file to my alias with absolute path like
$ lnn 4 /var/test.txt
Still same error as above.
But, if I enter the command of the alias in the terminal like
sed -n "4p" < test.txt
It works perfectly fine. No matter if I put quotes around test.txt
And here is another weird thing: If I write
alias lnn='sed -n "${1}p" < ${2}'
without the quotes around ${2} I get the error
-bash: ${2}: ambiguous redirect
In the terminal it works just fine...
So, what am I doing wrong? Does anyone have an idea on this? I'd love to know my mistake. But as I said, this isn't a real problem as I'm just curious why bash behaves like that.

Aliases in bash do not take parameters of any form. Save the pain and use a function instead.
function lnn() {
sed -n "${1}p" < "${2}"
}
Add the function to the file 00-alias.sh and source it once before calling the function from command-line.
source /etc/profile.d/00-alias.sh
lnn 4 test.txt
See more information at BashFAQ/80: How can I make an alias that takes an argument?
You can't. Aliases in bash are extremely rudimentary, and not really suitable to any serious purpose. The bash man page even says so explicitly:
An excerpt from the GNU bash man page, about aliases
.. There is no mechanism for using arguments in the replacement text. If arguments are needed, a shell function should be used.
On a side note the problem has nothing to do with relative paths (or) so, just remember aliases are not allowed in scripts. They're only allowed in interactive shells. If you're writing a script, always use a function instead.

Related

Alias to Scrot program doesn't work in .bashrc but works in terminal

I'm trying to alias a scrot command in .bashrc with this:
alias scrotn="scrot %Y-%m-%d-%s_$wx$h.jpg -e 'mv $f ~/pictures/screenshots/'"
The scrot command works in my terminal but when I try to run scrotn i receive this output:
mv: missing destination file operand after '/home/lain/pictures/screenshots/'
Already tried adding quotes to %Y-%m-%d-%s_$wx$h.jpg, switching double and single quotes and using /home/lain/ instead of ~/. Yes, ~/pictures/screenshots/ exists. I wanna create an alias to bind it to the PrtSc key in my DWM config.
Sorry for poor english.
Since the alias is defined as a double-quoted string (the inner quotes do not matter for the shell) $f is expanded (presumably to the empty string) when the alias is created. The recommended way to work around this would be to use a function rather than an alias. Aliases are considered deprecated by many, because they can do less than functions, are hard to debug as you've discovered, and are really not much simpler than functions.
The result:
scrotn() {
[your scrot command line]
}

How to use grep to find data conveniently and quickly

I am asking for general opinions on how to find files quickly.
One typical scenario is that I often need to grep some names from a PeopleNameFile.txt from Dir1. When I'm in a different directory, I am forced to grep the file with a long directory path. It would be nice to just do GREP "Warren Buffett" PeopleNameFile.txt.
BTW, I'm using Cygwin but I welcome any suggestions.
You can easily write a simple bash function in your ~/.bashrc:
function grnm() {
grep "$#" /path/to/peoplenamefile.txt
}
Then later on, at command line you can type:
$ grnm "Warren Buffet"
the nice thing is that you can actually include other grep parameters if you like as in:
$ grnm -i "warren buffet"
(The $ characters represent your shell prompt, not part of the command you type.)
When you edit the .bashrc file FOR THE FIRST TIME you may have to source it in your existing open cygwin windows:
$ source ~/.bashrc
But if you open a new window you should not have to do that.
Good luck, have fun
You can create script my_grep.sh and add it somewhere to you path, with content like this:
#!/bin/bash
grep $1 path/to/Dir1/PeopleNameFile.txt
than you just type
my_grep.sh "Warren Buffett"
also you can use alias and bash's function,
but this require to edit "~/.bashrc".
Simplest option would be to setup an alias which would grep for that file using the absolute path. Not sure whether cygwin allows aliases though.
Probably the most common way to do these kind of things is to use environment variables
e.g.
PNF='/very/long/path/PeopleNameFile.txt'
grep "Warren Buffett" $PNF

Commands work from Shell script but not from command line?

I quickly searched for this before posting, but could not find any similar posts. Let me know if they exist.
The commands being executed seem very simple. A directory listing is used as the input for a function.
The directory contains a bunch of files named "epi1_mcf_0###.nii.gz"
Command-line version (bash is running when this is executed):
fslmerge -t output_file `ls epi1_mcf_0*.nii.gz`
Shell script version:
#!/bin/bash
fslmerge -t output_file `ls epi1_mcf_0*.nii.gz`
The command-line version fails, but the shell script one works perfectly.
The error message is specific to the function, but it's included anyway.
** ERROR (nifti_image_read): failed to find header file for 'epi1_mcf_0000.nii.gz'
** ERROR: nifti_image_open(epi1_mcf_0000.nii.gz): bad header info
Error: failed to open file epi1_mcf_0000.nii.gz
Cannot open volume epi1_mcf_0000.nii.gz for reading!
I have been very frustrated with this problem (less so after I figured out that there was a way to get the command to work).
Any help would be appreciated.
(Or is the general consensus that the problem should be looked for in the "fslmerge" function?)
`ls epi1_mcf_0*.nii.gz` is better written as simply epi1_mcf_0*.nii.gz. As in:
fslmerge -t output_file epi1_mcf_0*.nii.gz
The `ls` doesn't add anything.
Note: Posted as an answer instead of comment. The Markdown-lite comment parser choked on my `` `ls epi1_mcf_0*.nii.gz` `` markup.
(I mentioned this in a comment first, but I'll make an answer since it helped!)
Do you have any shell aliases defined? (Type alias) Those will affect commands typed at the command line, but not scripts.
Linux often has ls defined as ls --color. This may affect the output since the colour codes are sent as escape codes through the regular output stream. If you use ls --color=auto it will auto-detect whether its output is a terminal or not. From man ls:
By default, color is not used to distinguish types of files. That is
equivalent to using --color=none. Using the --color option without the
optional WHEN argument is equivalent to using --color=always. With
--color=auto, color codes are output only if standard output is connected to a terminal (tty).

Creating a shorter version of a bash command

i am novice to the Linux shell and had to recently start using it for work...i have now got used to the basic commands in bash to find my way around...however there are a lot of commands i find myself typing over and over again and its kind of a hassle to type them every time...so can anyone tell me how can i shorten the command syntax for ones i use frequently.
A very simple example, i use the ls -lh command often, though this is quite short but im just giving an example. Can I have something (a shell script may be) so that I can run it by typing just say lh.
I want to do it for more complex commands.
alias lh='ls -lh'
If you want to make this persistent across sessions, put it in your .bashrc file. Don't forget to run source .bashrc afterwards to make bash aware of the changes.
If you want to pass variables, an alias just isn't enough. You can make a function. As an example, consider the command lsall to list everything in a given directory (note this is just an example and thus very error prone):
function lsall
{
ls $1/*
}
$Ngets replaced with the Nth argument.
You would place the following alias in your .bashrc file:
alias lh='ls -lh'
Now lh is shorthand for ls -lh.
For more complicated tasks you could use a bash function. For example, on one of my machines I have a function which causes 'ls' to run after every successful 'cd':
cdls() {
builtin cd "$*" && ls
}
alias cd='cdls'
you can define aliases. For longer commands, use a function, put it into a library file and source it whenever you want to use your functions.
Just for the sake of completeness, since you want to learn bash: you could also write a function
lh() {
ls -lh "$#"
}
although I would never write that when a simple alias would do ;-)
;) Heh, I remember one problem when I was starting out on Linux, which is that I would ask questions like these, and people would diligently answer them, but no one would explain how to make such changes permanent, and so I found myself typing in a bunch of commands every time I opened a terminal.
So, even though others have accurately answered this question... if you want to make the change permanent, put the alias-line into your ~/.profile or ~/.bashrc file (~ = your home directory). It depends a bit on your distribution on which is run when, but I always try adding my aliases to ~/.profile first and if that doesn't work, then ~/.bashrc. One of them should work for sure.

How can I define a bash alias as a sequence of multiple commands? [duplicate]

This question already has answers here:
Multiple commands in an alias for bash
(10 answers)
Closed 4 years ago.
I know how to configure aliases in bash, but is there a way to configure an alias for a sequence of commands?
I.e say I want one command to change to a particular directory, then run another command.
In addition, is there a way to setup a command that runs "sudo mycommand", then enters the password? In the MS-DOS days I'd be looking for a .bat file but I'm unsure of the linux (or in this case Mac OSX) equivalent.
For chaining a sequence of commands, try this:
alias x='command1;command2;command3;'
Or you can do this:
alias x='command1 && command2 && command3'
The && makes it only execute subsequent commands if the previous returns successful.
Also for entering passwords interactively, or interfacing with other programs like that, check out expect. (http://expect.nist.gov/)
You mention BAT files so perhaps what you want is to write a shell script. If so then just enter the commands you want line-by-line into a file like so:
command1
command2
and ask bash to execute the file:
bash myscript.sh
If you want to be able to invoke the script directly without typing "bash" then add the following line as the first line of the file:
#! /bin/bash
command1
command2
Then mark the file as executable:
chmod 755 myscript.sh
Now you can run it just like any other executable:
./myscript.sh
Note that unix doesn't really care about file extensions. You can simply name the file "myscript" without the ".sh" extension if you like. It's that special first line that is important. For example, if you want to write your script in the Perl programming language instead of bash the first line would be:
#! /usr/bin/perl
That first line tells your shell what interpreter to invoke to execute your script.
Also, if you now copy your script into one of the directories listed in the $PATH environment variable then you can call it from anywhere by simply typing its file name:
myscript.sh
Even tab-completion works. Which is why I usually include a ~/bin directory in my $PATH so that I can easily install personal scripts. And best of all, once you have a bunch of personal scripts that you are used to having you can easily port them to any new unix machine by copying your personal ~/bin directory.
it's probably easier to define functions for these types of things than aliases, keeps things more readable if you want to do more than a command or two:
In your .bashrc
perform_my_command() {
pushd /some_dir
my_command "$#"
popd
}
Then on the command line you can simply do:
perform_my_command my_parameter my_other_parameter "my quoted parameter"
You could do anything you like in a function, call other functions, etc.
You may want to have a look at the Advanced Bash Scripting Guide for in depth knowledge.
For the alias you can use this:
alias sequence='command1 -args; command2 -args;'
or if the second command must be executed only if the first one succeeds use:
alias sequence='command1 -args && command2 -args'
Your best bet is probably a shell function instead of an alias if the logic becomes more complex or if you need to add parameters (though bash supports aliases parameters).
This function can be defined in your .profile or .bashrc. The subshell is to avoid changing your working directory.
function myfunc {
( cd /tmp; command )
}
then from your command prompt
$ myfunc
For your second question you can just add your command to /etc/sudoers (if you are completely sure of what you are doing)
myuser ALL = NOPASSWD: \
/bin/mycommand
Apropos multiple commands in a single alias, you can use one of the logical operators to combine them. Here's one to switch to a directory and do an ls on it
alias x="cd /tmp && ls -al"
Another option is to use a shell function. These are sh/zsh/bash commands. I don't know enough of other shells to be sure if they work.
As for the sudo thing, if you want that (although I don't think it's a good idea), the right way to go is to alter the /etc/sudoers file to get what you want.
You can embed the function declaration followed by the function in the alias itself, like so:
alias my_alias='f() { do_stuff_with "$#" (arguments)" ...; }; f'
The benefit of this approach over just declaring the function by itself is that you can have a peace of mind that your function is not going to be overriden by some other script you're sourcing (or using .), which might use its own helper under the same name.
E.g., Suppose you have a script init-my-workspace.sh that you're calling like . init-my-workspace.sh or source init-my-workspace.sh whose purpose is to set or export a bunch of environment variables (e.g., JAVA_HOME, PYTHON_PATH etc.). If you happen to have a function my_alias inside there, as well, then you're out of luck as the latest function declaration withing the same shell instance wins.
Conversely, aliases have separate namespace and even in case of name clash, they are looked up first. Therefore, for customization relevant to interactive usage, you should only ever use aliases.
Finally, note that the practice of putting all the aliases in the same place (e.g., ~/.bash_aliases) enables you to easily spot any name clashes.
you can also write a shell function; example for " cd " and "ls " combo here

Resources