Bash shadow a command - function with same name as command [duplicate]
Clash Royale CLAN TAG#URR8PPP
up vote
5
down vote
favorite
This question already has an answer here:
Running an executable in PATH with the same name as an existing function
3 answers
I have a function in my .bashrc
to automatically sudo to open files that aren't writable by me:
vim()
if [ -w "$1" ]; then
vim "$1"
else
sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
fi
When the file needs sudo, it works fine. When it doesn't, it recursively calls this function and uses 100% of 1 CPU until I C-C.
From this answer I see there are a few options, all of which I've tried. One actually works:
'vim' "$1" #fails
vim "$1" #fails
command vim "$1" #Works!
Why do the other options not work as I would expect them to?
(I know this is a duplicate, but it was very hard to find my answer on SO/SE with the current question titles, so I wanted to post a question with a title that I and others could've found by google searching)
bash command alias
marked as duplicate by muru, G-Man, Anthony Geoghegan, Jeff Schaller, schily Jul 11 at 12:27
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
add a comment |Â
up vote
5
down vote
favorite
This question already has an answer here:
Running an executable in PATH with the same name as an existing function
3 answers
I have a function in my .bashrc
to automatically sudo to open files that aren't writable by me:
vim()
if [ -w "$1" ]; then
vim "$1"
else
sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
fi
When the file needs sudo, it works fine. When it doesn't, it recursively calls this function and uses 100% of 1 CPU until I C-C.
From this answer I see there are a few options, all of which I've tried. One actually works:
'vim' "$1" #fails
vim "$1" #fails
command vim "$1" #Works!
Why do the other options not work as I would expect them to?
(I know this is a duplicate, but it was very hard to find my answer on SO/SE with the current question titles, so I wanted to post a question with a title that I and others could've found by google searching)
bash command alias
marked as duplicate by muru, G-Man, Anthony Geoghegan, Jeff Schaller, schily Jul 11 at 12:27
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
add a comment |Â
up vote
5
down vote
favorite
up vote
5
down vote
favorite
This question already has an answer here:
Running an executable in PATH with the same name as an existing function
3 answers
I have a function in my .bashrc
to automatically sudo to open files that aren't writable by me:
vim()
if [ -w "$1" ]; then
vim "$1"
else
sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
fi
When the file needs sudo, it works fine. When it doesn't, it recursively calls this function and uses 100% of 1 CPU until I C-C.
From this answer I see there are a few options, all of which I've tried. One actually works:
'vim' "$1" #fails
vim "$1" #fails
command vim "$1" #Works!
Why do the other options not work as I would expect them to?
(I know this is a duplicate, but it was very hard to find my answer on SO/SE with the current question titles, so I wanted to post a question with a title that I and others could've found by google searching)
bash command alias
This question already has an answer here:
Running an executable in PATH with the same name as an existing function
3 answers
I have a function in my .bashrc
to automatically sudo to open files that aren't writable by me:
vim()
if [ -w "$1" ]; then
vim "$1"
else
sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
fi
When the file needs sudo, it works fine. When it doesn't, it recursively calls this function and uses 100% of 1 CPU until I C-C.
From this answer I see there are a few options, all of which I've tried. One actually works:
'vim' "$1" #fails
vim "$1" #fails
command vim "$1" #Works!
Why do the other options not work as I would expect them to?
(I know this is a duplicate, but it was very hard to find my answer on SO/SE with the current question titles, so I wanted to post a question with a title that I and others could've found by google searching)
This question already has an answer here:
Running an executable in PATH with the same name as an existing function
3 answers
bash command alias
asked Jul 10 at 19:18
jeremysprofile
20910
20910
marked as duplicate by muru, G-Man, Anthony Geoghegan, Jeff Schaller, schily Jul 11 at 12:27
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
marked as duplicate by muru, G-Man, Anthony Geoghegan, Jeff Schaller, schily Jul 11 at 12:27
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
add a comment |Â
add a comment |Â
3 Answers
3
active
oldest
votes
up vote
6
down vote
accepted
The problem is that the first two options are designed only to deal with aliases. They are not special redirecting operators that can sense if you have a function or command with the same name. All they do is prevent expansion, which is what an alias tries to do
alias v='sudo vim'
v x.txt
#automatically expands "v" to "sudo vim" and then does the rest of the command
# v x.txt ==> sudo vim x.txt
Bash just tries to expand the first word of a command using the list of aliases it knows about (which you can get with alias
). Aliases don't take arguments, and can only be the first word (space-separated; vim x.txt
won't expand into sudo vimim x.txt
using the alias above) in a command to expand properly.
However, expansion never happens to things in single quotes: echo '$USER'
will print out a literal $USER
and not what the variable stands for. Also, bash expands x
to be x
(mostly). These aren't extra added-in ways specifically to escape an alias, they're just part of how bash expansion was written. Thus, you can use these methods to let an alias shadow a command and still have access to the actual command.
alias ls='ls -la'
ls foo.txt #will expand into ls -la foo.txt
ls foo.txt #will expand into ls foo.txt
'ls' foo.txt #same as above
'ls foo.txt' #same as above
However, they don't stop functions. A function doesn't need expansion to work, it is called with its name.
ls ()
echo "not an alias"
alias ls='echo "an alias"'
ls foo.txt #will echo "an alias"
ls foo.txt #will echo "not an alias"
'ls' foo.txt #will echo "not an alias"
command ls foo.txt #will actually run `ls`
add a comment |Â
up vote
1
down vote
If you have the function call the full path to the vim
binary, you won't get the recursive loop. Also, using the -H
option to sudo sets $HOME.
vim()
if [ -w "$1" ]; then
command vim "$1"
else
sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
fi
Thanks. I hadn't thought about this, but now I have it in my .bashrc
, too.
EDITED:
updated calling command vim
and sudo env
instead of sudo -h
. this way there is no loop to stay in.
1
sudo -H
does not work here (I tried it). It sets$HOME
equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
â jeremysprofile
Jul 10 at 22:14
2
I would suggestcommand vim
as a more generic (PATH
-respecting) approach, rather than/usr/bin/vim
.
â Charles Duffy
Jul 10 at 23:38
@CharlesDuffy thanks for the tip. I have added that to the answer.
â Tim Kennedy
Jul 11 at 1:11
@jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
â Tim Kennedy
Jul 11 at 1:14
add a comment |Â
up vote
0
down vote
Why do the other options not work as I would expect them to?
Because there are some details in the way a command line gets executed.
The basic concept is that the first word of a command line is the command that the shell will search and try to execute, in this order:
The command line is split on metacharacters (mostly):
metacharacters
A character that, when unquoted, separates words. One of the following:
| & ; ( ) < > space tab newlineIf the first unquoted word match an alias word, it is replaced by the alias definition. To avoid loops, this is executed only once if the new first word match an alias being expanded. This makes this work without loops:
$ alias ls='ls -la'
$ ls dir # will expand to `ls -la dir`Note that: If the last character of the alias value is a blank, then the next command word following the alias is also checked for alias expansion.
If the (resulting) first word is an expansion (like a
$var
), it is replaced (and subject to IFS word splitting (and globing) if not quoted):$ var='echo -e '
$ $var test 'twords' # will expand to `echo -e test words`
test wordsThe resulting first word after above expansions (after optional variable assignments) will be searched in this order:
- special builtins (only in POSIX mode)
- functions
- builtins
- hashed commands (read help hash)
- external commands (searched in
$PATH
dirs)
The first match found will be executed.
The order may be changed by (for test):
- To bypass just alias, use
test
or any other kind of expansion. - To ignore functions and aliases, use
command test
. - To execute a builtin, use
builtin test
. - To execute an external application, use an explicit path:
/bin/test
.
So, a function defined as:
$ ls() ls;
Will be called in an infinite loop. As also an script which calls the same first word. Something like:
#!/bin/bash
$0 "$@"
Will re-execute the same script in a loop until the kernel breaks out (if the kernel in use has a limit of successive calls to scripts).
The order will be shown by running type -a
:
$ test() echo test function;
$ alias test=test
$ type -a
test is aliased to `test'
test is a function
test ()
echo test function
test is a shell builtin
test is /usr/bin/test
The function (as defined in the question) will only bypass the alias with vim
but not the function. To bypass alias and functions, use command. This function should do what you need:
vim()
if [ -w "$1" ]; then
command vim "$1"
else
sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
fi
This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) namedtouch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''
; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
â Charles Duffy
Jul 11 at 12:07
Consider insteadsudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
-- then the~
is evaluated by the parent shell beforesudo
even starts, and you're passing the filename out-of-band from the script text.
â Charles Duffy
Jul 11 at 12:09
(er, should have been "withtouch
..." rather than "namedtouch
..." above).
â Charles Duffy
Jul 11 at 12:14
@CharlesDuffy Sure, that's correct, thanks.
â Isaac
Jul 11 at 13:44
add a comment |Â
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
6
down vote
accepted
The problem is that the first two options are designed only to deal with aliases. They are not special redirecting operators that can sense if you have a function or command with the same name. All they do is prevent expansion, which is what an alias tries to do
alias v='sudo vim'
v x.txt
#automatically expands "v" to "sudo vim" and then does the rest of the command
# v x.txt ==> sudo vim x.txt
Bash just tries to expand the first word of a command using the list of aliases it knows about (which you can get with alias
). Aliases don't take arguments, and can only be the first word (space-separated; vim x.txt
won't expand into sudo vimim x.txt
using the alias above) in a command to expand properly.
However, expansion never happens to things in single quotes: echo '$USER'
will print out a literal $USER
and not what the variable stands for. Also, bash expands x
to be x
(mostly). These aren't extra added-in ways specifically to escape an alias, they're just part of how bash expansion was written. Thus, you can use these methods to let an alias shadow a command and still have access to the actual command.
alias ls='ls -la'
ls foo.txt #will expand into ls -la foo.txt
ls foo.txt #will expand into ls foo.txt
'ls' foo.txt #same as above
'ls foo.txt' #same as above
However, they don't stop functions. A function doesn't need expansion to work, it is called with its name.
ls ()
echo "not an alias"
alias ls='echo "an alias"'
ls foo.txt #will echo "an alias"
ls foo.txt #will echo "not an alias"
'ls' foo.txt #will echo "not an alias"
command ls foo.txt #will actually run `ls`
add a comment |Â
up vote
6
down vote
accepted
The problem is that the first two options are designed only to deal with aliases. They are not special redirecting operators that can sense if you have a function or command with the same name. All they do is prevent expansion, which is what an alias tries to do
alias v='sudo vim'
v x.txt
#automatically expands "v" to "sudo vim" and then does the rest of the command
# v x.txt ==> sudo vim x.txt
Bash just tries to expand the first word of a command using the list of aliases it knows about (which you can get with alias
). Aliases don't take arguments, and can only be the first word (space-separated; vim x.txt
won't expand into sudo vimim x.txt
using the alias above) in a command to expand properly.
However, expansion never happens to things in single quotes: echo '$USER'
will print out a literal $USER
and not what the variable stands for. Also, bash expands x
to be x
(mostly). These aren't extra added-in ways specifically to escape an alias, they're just part of how bash expansion was written. Thus, you can use these methods to let an alias shadow a command and still have access to the actual command.
alias ls='ls -la'
ls foo.txt #will expand into ls -la foo.txt
ls foo.txt #will expand into ls foo.txt
'ls' foo.txt #same as above
'ls foo.txt' #same as above
However, they don't stop functions. A function doesn't need expansion to work, it is called with its name.
ls ()
echo "not an alias"
alias ls='echo "an alias"'
ls foo.txt #will echo "an alias"
ls foo.txt #will echo "not an alias"
'ls' foo.txt #will echo "not an alias"
command ls foo.txt #will actually run `ls`
add a comment |Â
up vote
6
down vote
accepted
up vote
6
down vote
accepted
The problem is that the first two options are designed only to deal with aliases. They are not special redirecting operators that can sense if you have a function or command with the same name. All they do is prevent expansion, which is what an alias tries to do
alias v='sudo vim'
v x.txt
#automatically expands "v" to "sudo vim" and then does the rest of the command
# v x.txt ==> sudo vim x.txt
Bash just tries to expand the first word of a command using the list of aliases it knows about (which you can get with alias
). Aliases don't take arguments, and can only be the first word (space-separated; vim x.txt
won't expand into sudo vimim x.txt
using the alias above) in a command to expand properly.
However, expansion never happens to things in single quotes: echo '$USER'
will print out a literal $USER
and not what the variable stands for. Also, bash expands x
to be x
(mostly). These aren't extra added-in ways specifically to escape an alias, they're just part of how bash expansion was written. Thus, you can use these methods to let an alias shadow a command and still have access to the actual command.
alias ls='ls -la'
ls foo.txt #will expand into ls -la foo.txt
ls foo.txt #will expand into ls foo.txt
'ls' foo.txt #same as above
'ls foo.txt' #same as above
However, they don't stop functions. A function doesn't need expansion to work, it is called with its name.
ls ()
echo "not an alias"
alias ls='echo "an alias"'
ls foo.txt #will echo "an alias"
ls foo.txt #will echo "not an alias"
'ls' foo.txt #will echo "not an alias"
command ls foo.txt #will actually run `ls`
The problem is that the first two options are designed only to deal with aliases. They are not special redirecting operators that can sense if you have a function or command with the same name. All they do is prevent expansion, which is what an alias tries to do
alias v='sudo vim'
v x.txt
#automatically expands "v" to "sudo vim" and then does the rest of the command
# v x.txt ==> sudo vim x.txt
Bash just tries to expand the first word of a command using the list of aliases it knows about (which you can get with alias
). Aliases don't take arguments, and can only be the first word (space-separated; vim x.txt
won't expand into sudo vimim x.txt
using the alias above) in a command to expand properly.
However, expansion never happens to things in single quotes: echo '$USER'
will print out a literal $USER
and not what the variable stands for. Also, bash expands x
to be x
(mostly). These aren't extra added-in ways specifically to escape an alias, they're just part of how bash expansion was written. Thus, you can use these methods to let an alias shadow a command and still have access to the actual command.
alias ls='ls -la'
ls foo.txt #will expand into ls -la foo.txt
ls foo.txt #will expand into ls foo.txt
'ls' foo.txt #same as above
'ls foo.txt' #same as above
However, they don't stop functions. A function doesn't need expansion to work, it is called with its name.
ls ()
echo "not an alias"
alias ls='echo "an alias"'
ls foo.txt #will echo "an alias"
ls foo.txt #will echo "not an alias"
'ls' foo.txt #will echo "not an alias"
command ls foo.txt #will actually run `ls`
answered Jul 10 at 19:18
jeremysprofile
20910
20910
add a comment |Â
add a comment |Â
up vote
1
down vote
If you have the function call the full path to the vim
binary, you won't get the recursive loop. Also, using the -H
option to sudo sets $HOME.
vim()
if [ -w "$1" ]; then
command vim "$1"
else
sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
fi
Thanks. I hadn't thought about this, but now I have it in my .bashrc
, too.
EDITED:
updated calling command vim
and sudo env
instead of sudo -h
. this way there is no loop to stay in.
1
sudo -H
does not work here (I tried it). It sets$HOME
equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
â jeremysprofile
Jul 10 at 22:14
2
I would suggestcommand vim
as a more generic (PATH
-respecting) approach, rather than/usr/bin/vim
.
â Charles Duffy
Jul 10 at 23:38
@CharlesDuffy thanks for the tip. I have added that to the answer.
â Tim Kennedy
Jul 11 at 1:11
@jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
â Tim Kennedy
Jul 11 at 1:14
add a comment |Â
up vote
1
down vote
If you have the function call the full path to the vim
binary, you won't get the recursive loop. Also, using the -H
option to sudo sets $HOME.
vim()
if [ -w "$1" ]; then
command vim "$1"
else
sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
fi
Thanks. I hadn't thought about this, but now I have it in my .bashrc
, too.
EDITED:
updated calling command vim
and sudo env
instead of sudo -h
. this way there is no loop to stay in.
1
sudo -H
does not work here (I tried it). It sets$HOME
equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
â jeremysprofile
Jul 10 at 22:14
2
I would suggestcommand vim
as a more generic (PATH
-respecting) approach, rather than/usr/bin/vim
.
â Charles Duffy
Jul 10 at 23:38
@CharlesDuffy thanks for the tip. I have added that to the answer.
â Tim Kennedy
Jul 11 at 1:11
@jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
â Tim Kennedy
Jul 11 at 1:14
add a comment |Â
up vote
1
down vote
up vote
1
down vote
If you have the function call the full path to the vim
binary, you won't get the recursive loop. Also, using the -H
option to sudo sets $HOME.
vim()
if [ -w "$1" ]; then
command vim "$1"
else
sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
fi
Thanks. I hadn't thought about this, but now I have it in my .bashrc
, too.
EDITED:
updated calling command vim
and sudo env
instead of sudo -h
. this way there is no loop to stay in.
If you have the function call the full path to the vim
binary, you won't get the recursive loop. Also, using the -H
option to sudo sets $HOME.
vim()
if [ -w "$1" ]; then
command vim "$1"
else
sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
fi
Thanks. I hadn't thought about this, but now I have it in my .bashrc
, too.
EDITED:
updated calling command vim
and sudo env
instead of sudo -h
. this way there is no loop to stay in.
edited Jul 11 at 1:09
answered Jul 10 at 21:55
Tim Kennedy
13.1k22848
13.1k22848
1
sudo -H
does not work here (I tried it). It sets$HOME
equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
â jeremysprofile
Jul 10 at 22:14
2
I would suggestcommand vim
as a more generic (PATH
-respecting) approach, rather than/usr/bin/vim
.
â Charles Duffy
Jul 10 at 23:38
@CharlesDuffy thanks for the tip. I have added that to the answer.
â Tim Kennedy
Jul 11 at 1:11
@jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
â Tim Kennedy
Jul 11 at 1:14
add a comment |Â
1
sudo -H
does not work here (I tried it). It sets$HOME
equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
â jeremysprofile
Jul 10 at 22:14
2
I would suggestcommand vim
as a more generic (PATH
-respecting) approach, rather than/usr/bin/vim
.
â Charles Duffy
Jul 10 at 23:38
@CharlesDuffy thanks for the tip. I have added that to the answer.
â Tim Kennedy
Jul 11 at 1:11
@jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
â Tim Kennedy
Jul 11 at 1:14
1
1
sudo -H
does not work here (I tried it). It sets $HOME
equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggeredâ jeremysprofile
Jul 10 at 22:14
sudo -H
does not work here (I tried it). It sets $HOME
equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggeredâ jeremysprofile
Jul 10 at 22:14
2
2
I would suggest
command vim
as a more generic (PATH
-respecting) approach, rather than /usr/bin/vim
.â Charles Duffy
Jul 10 at 23:38
I would suggest
command vim
as a more generic (PATH
-respecting) approach, rather than /usr/bin/vim
.â Charles Duffy
Jul 10 at 23:38
@CharlesDuffy thanks for the tip. I have added that to the answer.
â Tim Kennedy
Jul 11 at 1:11
@CharlesDuffy thanks for the tip. I have added that to the answer.
â Tim Kennedy
Jul 11 at 1:11
@jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
â Tim Kennedy
Jul 11 at 1:14
@jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
â Tim Kennedy
Jul 11 at 1:14
add a comment |Â
up vote
0
down vote
Why do the other options not work as I would expect them to?
Because there are some details in the way a command line gets executed.
The basic concept is that the first word of a command line is the command that the shell will search and try to execute, in this order:
The command line is split on metacharacters (mostly):
metacharacters
A character that, when unquoted, separates words. One of the following:
| & ; ( ) < > space tab newlineIf the first unquoted word match an alias word, it is replaced by the alias definition. To avoid loops, this is executed only once if the new first word match an alias being expanded. This makes this work without loops:
$ alias ls='ls -la'
$ ls dir # will expand to `ls -la dir`Note that: If the last character of the alias value is a blank, then the next command word following the alias is also checked for alias expansion.
If the (resulting) first word is an expansion (like a
$var
), it is replaced (and subject to IFS word splitting (and globing) if not quoted):$ var='echo -e '
$ $var test 'twords' # will expand to `echo -e test words`
test wordsThe resulting first word after above expansions (after optional variable assignments) will be searched in this order:
- special builtins (only in POSIX mode)
- functions
- builtins
- hashed commands (read help hash)
- external commands (searched in
$PATH
dirs)
The first match found will be executed.
The order may be changed by (for test):
- To bypass just alias, use
test
or any other kind of expansion. - To ignore functions and aliases, use
command test
. - To execute a builtin, use
builtin test
. - To execute an external application, use an explicit path:
/bin/test
.
So, a function defined as:
$ ls() ls;
Will be called in an infinite loop. As also an script which calls the same first word. Something like:
#!/bin/bash
$0 "$@"
Will re-execute the same script in a loop until the kernel breaks out (if the kernel in use has a limit of successive calls to scripts).
The order will be shown by running type -a
:
$ test() echo test function;
$ alias test=test
$ type -a
test is aliased to `test'
test is a function
test ()
echo test function
test is a shell builtin
test is /usr/bin/test
The function (as defined in the question) will only bypass the alias with vim
but not the function. To bypass alias and functions, use command. This function should do what you need:
vim()
if [ -w "$1" ]; then
command vim "$1"
else
sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
fi
This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) namedtouch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''
; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
â Charles Duffy
Jul 11 at 12:07
Consider insteadsudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
-- then the~
is evaluated by the parent shell beforesudo
even starts, and you're passing the filename out-of-band from the script text.
â Charles Duffy
Jul 11 at 12:09
(er, should have been "withtouch
..." rather than "namedtouch
..." above).
â Charles Duffy
Jul 11 at 12:14
@CharlesDuffy Sure, that's correct, thanks.
â Isaac
Jul 11 at 13:44
add a comment |Â
up vote
0
down vote
Why do the other options not work as I would expect them to?
Because there are some details in the way a command line gets executed.
The basic concept is that the first word of a command line is the command that the shell will search and try to execute, in this order:
The command line is split on metacharacters (mostly):
metacharacters
A character that, when unquoted, separates words. One of the following:
| & ; ( ) < > space tab newlineIf the first unquoted word match an alias word, it is replaced by the alias definition. To avoid loops, this is executed only once if the new first word match an alias being expanded. This makes this work without loops:
$ alias ls='ls -la'
$ ls dir # will expand to `ls -la dir`Note that: If the last character of the alias value is a blank, then the next command word following the alias is also checked for alias expansion.
If the (resulting) first word is an expansion (like a
$var
), it is replaced (and subject to IFS word splitting (and globing) if not quoted):$ var='echo -e '
$ $var test 'twords' # will expand to `echo -e test words`
test wordsThe resulting first word after above expansions (after optional variable assignments) will be searched in this order:
- special builtins (only in POSIX mode)
- functions
- builtins
- hashed commands (read help hash)
- external commands (searched in
$PATH
dirs)
The first match found will be executed.
The order may be changed by (for test):
- To bypass just alias, use
test
or any other kind of expansion. - To ignore functions and aliases, use
command test
. - To execute a builtin, use
builtin test
. - To execute an external application, use an explicit path:
/bin/test
.
So, a function defined as:
$ ls() ls;
Will be called in an infinite loop. As also an script which calls the same first word. Something like:
#!/bin/bash
$0 "$@"
Will re-execute the same script in a loop until the kernel breaks out (if the kernel in use has a limit of successive calls to scripts).
The order will be shown by running type -a
:
$ test() echo test function;
$ alias test=test
$ type -a
test is aliased to `test'
test is a function
test ()
echo test function
test is a shell builtin
test is /usr/bin/test
The function (as defined in the question) will only bypass the alias with vim
but not the function. To bypass alias and functions, use command. This function should do what you need:
vim()
if [ -w "$1" ]; then
command vim "$1"
else
sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
fi
This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) namedtouch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''
; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
â Charles Duffy
Jul 11 at 12:07
Consider insteadsudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
-- then the~
is evaluated by the parent shell beforesudo
even starts, and you're passing the filename out-of-band from the script text.
â Charles Duffy
Jul 11 at 12:09
(er, should have been "withtouch
..." rather than "namedtouch
..." above).
â Charles Duffy
Jul 11 at 12:14
@CharlesDuffy Sure, that's correct, thanks.
â Isaac
Jul 11 at 13:44
add a comment |Â
up vote
0
down vote
up vote
0
down vote
Why do the other options not work as I would expect them to?
Because there are some details in the way a command line gets executed.
The basic concept is that the first word of a command line is the command that the shell will search and try to execute, in this order:
The command line is split on metacharacters (mostly):
metacharacters
A character that, when unquoted, separates words. One of the following:
| & ; ( ) < > space tab newlineIf the first unquoted word match an alias word, it is replaced by the alias definition. To avoid loops, this is executed only once if the new first word match an alias being expanded. This makes this work without loops:
$ alias ls='ls -la'
$ ls dir # will expand to `ls -la dir`Note that: If the last character of the alias value is a blank, then the next command word following the alias is also checked for alias expansion.
If the (resulting) first word is an expansion (like a
$var
), it is replaced (and subject to IFS word splitting (and globing) if not quoted):$ var='echo -e '
$ $var test 'twords' # will expand to `echo -e test words`
test wordsThe resulting first word after above expansions (after optional variable assignments) will be searched in this order:
- special builtins (only in POSIX mode)
- functions
- builtins
- hashed commands (read help hash)
- external commands (searched in
$PATH
dirs)
The first match found will be executed.
The order may be changed by (for test):
- To bypass just alias, use
test
or any other kind of expansion. - To ignore functions and aliases, use
command test
. - To execute a builtin, use
builtin test
. - To execute an external application, use an explicit path:
/bin/test
.
So, a function defined as:
$ ls() ls;
Will be called in an infinite loop. As also an script which calls the same first word. Something like:
#!/bin/bash
$0 "$@"
Will re-execute the same script in a loop until the kernel breaks out (if the kernel in use has a limit of successive calls to scripts).
The order will be shown by running type -a
:
$ test() echo test function;
$ alias test=test
$ type -a
test is aliased to `test'
test is a function
test ()
echo test function
test is a shell builtin
test is /usr/bin/test
The function (as defined in the question) will only bypass the alias with vim
but not the function. To bypass alias and functions, use command. This function should do what you need:
vim()
if [ -w "$1" ]; then
command vim "$1"
else
sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
fi
Why do the other options not work as I would expect them to?
Because there are some details in the way a command line gets executed.
The basic concept is that the first word of a command line is the command that the shell will search and try to execute, in this order:
The command line is split on metacharacters (mostly):
metacharacters
A character that, when unquoted, separates words. One of the following:
| & ; ( ) < > space tab newlineIf the first unquoted word match an alias word, it is replaced by the alias definition. To avoid loops, this is executed only once if the new first word match an alias being expanded. This makes this work without loops:
$ alias ls='ls -la'
$ ls dir # will expand to `ls -la dir`Note that: If the last character of the alias value is a blank, then the next command word following the alias is also checked for alias expansion.
If the (resulting) first word is an expansion (like a
$var
), it is replaced (and subject to IFS word splitting (and globing) if not quoted):$ var='echo -e '
$ $var test 'twords' # will expand to `echo -e test words`
test wordsThe resulting first word after above expansions (after optional variable assignments) will be searched in this order:
- special builtins (only in POSIX mode)
- functions
- builtins
- hashed commands (read help hash)
- external commands (searched in
$PATH
dirs)
The first match found will be executed.
The order may be changed by (for test):
- To bypass just alias, use
test
or any other kind of expansion. - To ignore functions and aliases, use
command test
. - To execute a builtin, use
builtin test
. - To execute an external application, use an explicit path:
/bin/test
.
So, a function defined as:
$ ls() ls;
Will be called in an infinite loop. As also an script which calls the same first word. Something like:
#!/bin/bash
$0 "$@"
Will re-execute the same script in a loop until the kernel breaks out (if the kernel in use has a limit of successive calls to scripts).
The order will be shown by running type -a
:
$ test() echo test function;
$ alias test=test
$ type -a
test is aliased to `test'
test is a function
test ()
echo test function
test is a shell builtin
test is /usr/bin/test
The function (as defined in the question) will only bypass the alias with vim
but not the function. To bypass alias and functions, use command. This function should do what you need:
vim()
if [ -w "$1" ]; then
command vim "$1"
else
sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
fi
edited Jul 11 at 13:42
answered Jul 11 at 2:42
Isaac
6,2331632
6,2331632
This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) namedtouch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''
; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
â Charles Duffy
Jul 11 at 12:07
Consider insteadsudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
-- then the~
is evaluated by the parent shell beforesudo
even starts, and you're passing the filename out-of-band from the script text.
â Charles Duffy
Jul 11 at 12:09
(er, should have been "withtouch
..." rather than "namedtouch
..." above).
â Charles Duffy
Jul 11 at 12:14
@CharlesDuffy Sure, that's correct, thanks.
â Isaac
Jul 11 at 13:44
add a comment |Â
This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) namedtouch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''
; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
â Charles Duffy
Jul 11 at 12:07
Consider insteadsudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
-- then the~
is evaluated by the parent shell beforesudo
even starts, and you're passing the filename out-of-band from the script text.
â Charles Duffy
Jul 11 at 12:09
(er, should have been "withtouch
..." rather than "namedtouch
..." above).
â Charles Duffy
Jul 11 at 12:14
@CharlesDuffy Sure, that's correct, thanks.
â Isaac
Jul 11 at 13:44
This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) named
touch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''
; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.â Charles Duffy
Jul 11 at 12:07
This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) named
touch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''
; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.â Charles Duffy
Jul 11 at 12:07
Consider instead
sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
-- then the ~
is evaluated by the parent shell before sudo
even starts, and you're passing the filename out-of-band from the script text.â Charles Duffy
Jul 11 at 12:09
Consider instead
sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
-- then the ~
is evaluated by the parent shell before sudo
even starts, and you're passing the filename out-of-band from the script text.â Charles Duffy
Jul 11 at 12:09
(er, should have been "with
touch
..." rather than "named touch
..." above).â Charles Duffy
Jul 11 at 12:14
(er, should have been "with
touch
..." rather than "named touch
..." above).â Charles Duffy
Jul 11 at 12:14
@CharlesDuffy Sure, that's correct, thanks.
â Isaac
Jul 11 at 13:44
@CharlesDuffy Sure, that's correct, thanks.
â Isaac
Jul 11 at 13:44
add a comment |Â