LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Software
User Name
Password
Linux - Software This forum is for Software issues.
Having a problem installing a new program? Want to know which application is best for the job? Post your question in this forum.

Notices


Reply
  Search this Thread
Old 05-14-2012, 02:35 PM   #1
markseger
Member
 
Registered: Jul 2003
Posts: 244

Rep: Reputation: 26
looking for some bash shell magic with globbing


I realize by default that when I execute a command like "xyz *", the shell expands the * and to disable that behavior I can do one of two things:
- quote the * like this: xyz '*'
- turn off word expansion like this: set -f, before running my command

I prefer the latter because expecting users to use quotes is a pain and it in fact works just fine. BUT...

The trick is how to turn expansion back on after I execute xyz! I first tried an alias that did something like this:

cmd='set -f; xyz, set +f'

which works fine as long as xyz doesn't take any arguments, which it does! so then I thought I could turn off expansion in the alias and turn it back on from within xyz, which happens to be a perl script, something like:

`set +f`

but that would essentially start a new process and so the set would affect that process, not the one my perl script is running in.

has anyone successfully done this sort of thing? I gotta believe there's a standard way to do it.

-mark
 
Old 05-14-2012, 03:30 PM   #2
colucix
LQ Guru
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509

Rep: Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981
You can try a shell function:
Code:
function xyz() {
  set -f
  /path/to/xyz $@
  set +f
}
export -f xyz
The function takes precedence over the command, so that you have to specify the full path of the command inside. Put the definition in a system-wide configuration file (e.g. /etc/bashrc, but the exact name depends on your system) and the trick is done.
 
Old 05-14-2012, 03:36 PM   #3
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian + kde 4 / 5
Posts: 6,848

Rep: Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012
Nope, I can't think of any way to do it cleanly. As you've discovered, aliases only work to replace the initial command name with the substituted string, and there's no way to arrange arguments in arbitrary order.

The usual way to set up such commands is with a function, but in this case, the globs would still have to be quoted in order to pass them to the function unexpanded first, thus defeating the whole purpose of it. (sorry colucix )

The only option I can suggest that even comes close is to put set +f in your PROMPT_COMMAND variable, and then it would be executed after every command execution. Of course that would mean you could never keep globbing unset for more than a single command.

The final suggestion I could make is just to set up a simple command for quickly toggling the setting on and off, which you can run just before and after your xyz command:

Code:
# "gt" stands for "glob toggle"

alias gt='[[ $- == *f* ]] && { set +f ; echo "globbing on" ;} || { set -f ; echo "globbing off" ;}'

# a function would be more robust, however.

gt() { if [[ $- == *f* ]]; then set +f && echo "globbing on" ; else set -f && echo "globbing off" ; fi ; }

PS: Please use [code][/code] tags around your code and data, to preserve formatting and to improve readability. Please do not use quote tags, colors, or other fancy formatting.
 
Old 05-14-2012, 03:51 PM   #4
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian + kde 4 / 5
Posts: 6,848

Rep: Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012
Hold on, I just had an inspiration. A combination of alias and function just might do the trick.

Code:
alias xyz='set -f ; xyzfunc'

xyzfunc() { command xyz "$@" ; set +f ; }
The alias turns off globbing, and runs the function, and the function runs the actual command, then follows up by resetting globbing.

Last edited by David the H.; 05-14-2012 at 04:00 PM. Reason: changed to a more reasonable function name
 
Old 05-14-2012, 04:12 PM   #5
markseger
Member
 
Registered: Jul 2003
Posts: 244

Original Poster
Rep: Reputation: 26
I'm not sure I follow, but I think understand functionally what you're suggesting and it sounds promising. what is exactly is xyzfunc? a one line script? I'm not sure what it would consist of. who calls the xyzfunc function?

just to give a little perspective, I'm trying to build my own ls command, called sls, that does different sorts of things but still supports wild cards. so using your syntax if I build an alias like:

alias sls='set -f; xyzfunc'

what is the name of the file with xyzfunc in it and what exactly is in it?

-mark
 
Old 05-14-2012, 04:38 PM   #6
colucix
LQ Guru
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509

Rep: Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981Reputation: 1981
Quote:
Originally Posted by David the H. View Post
The usual way to set up such commands is with a function, but in this case, the globs would still have to be quoted in order to pass them to the function unexpanded first, thus defeating the whole purpose of it. (sorry colucix )
You're absolutely right, David. I should have mistaken my tests!
 
Old 05-15-2012, 01:56 AM   #7
chrism01
LQ Guru
 
Registered: Aug 2004
Location: Sydney
Distribution: Centos 7.7 (?), Centos 8.1
Posts: 17,817

Rep: Reputation: 2553Reputation: 2553Reputation: 2553Reputation: 2553Reputation: 2553Reputation: 2553Reputation: 2553Reputation: 2553Reputation: 2553Reputation: 2553Reputation: 2553
It seems to me this does what you want
Code:
set -f && ./t.sh * && set +f
P1 = *
where the content of t.sh is
Code:
echo "P1 = $1"
In other words it turns off globbing, passes the wildcard to the called prog un-expanded, then resets globbing in calling env.
 
Old 05-15-2012, 07:22 AM   #8
markseger
Member
 
Registered: Jul 2003
Posts: 244

Original Poster
Rep: Reputation: 26
> set -f && ./t.sh * && set +f

That's not what I want to do. I want to pass $1 to my script. Sometimes it will be an * and sometimes not. But I get the general idea and hopefully can get it to do what I want. Thanks.
-mark
 
Old 05-15-2012, 08:39 AM   #9
markseger
Member
 
Registered: Jul 2003
Posts: 244

Original Poster
Rep: Reputation: 26
I keep going back and forth on this. One minute I think it's working and the next it's not. I think the problem is it's a different process resetting f and so that applies to the new process, not the original one. In other words, I don't think it works.

If someone could get this to work I'd love to see an example.

-mark

Last edited by markseger; 05-15-2012 at 09:00 AM.
 
Old 05-15-2012, 09:10 AM   #10
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian + kde 4 / 5
Posts: 6,848

Rep: Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012Reputation: 2012
Quote:
Originally Posted by markseger View Post
I'm not sure I follow, but I think understand functionally what you're suggesting and it sounds promising. what is exactly is xyzfunc? a one line script? I'm not sure what it would consist of. who calls the xyzfunc function?
It's really not that complicated, actually. It uses a shell function, a block of code, sort of a mini-script, that resides in memory rather than a file. They are generally much more flexible and robust than a simple alias, since they can accept and process input parameters just like a script can.

The main problem you have here is that you need to alter the environment of the parent shell before the command line that executes the main script is parsed, and then again afterwards. The only way you can do this is with a command that is executed inside that shell itself; there's no way to do it from inside a script's sub-environment.

This means you must use aliases and/or functions. And the only way I can think of to create a one-word command that will do exactly what you want in the main shell is to use both.

Only an alias is capable of expanding a single command name into multiple commands before the rest of the line is parsed (meaning it's the only way you can turn off globbing). So we have to start with an alias. But as you know, it is then subsequently unable run another command after any arguments that follow it.

So what we do is use the alias to launch a function instead, and use that function to run the subsequent commands in the required order.

To break it down...

Code:
alias xyz='set -f ; xyzfunc'
With this alias, when you enter, for example, the line:

Code:
xyz *.txt *.jpg
...it will replace the word "xyz" at the front with the defined substitution string, so the line you are actually running is this:

Code:
set -f ; xyzfunc *.txt *.jpg
Since this expands to two separate commands, globbing can be disabled before the second command is run, and the arguments remain literal. So the alias executes the xyzfunc function, with the two literal globbing values as arguments.

Now we've also previously defined the function xyzfunc like this:

Code:
xyzfunc() { command xyz "$@" ; set +f ; }
(just put it in your shell startup file along with the alias.)

"xyz" here is now the name of the actual script you want to run. The command keyword is added to ensure that it doesn't do any recursive processing of the alias name, and just runs the script directly. "$@" expands to a string of all the parameters passed to the function; here they become arguments to xyz. Since and "$@" is quoted (not to mention globbing is still off), they remain literal the whole time.

So the function launches two commands in sequence:

Code:
xyz *.txt *.jpg
set +f
...and so globbing is turned back on before it exits.

Last edited by David the H.; 05-15-2012 at 09:17 AM.
 
1 members found this post helpful.
Old 05-15-2012, 10:47 AM   #11
markseger
Member
 
Registered: Jul 2003
Posts: 244

Original Poster
Rep: Reputation: 26
outstanding. I didn't realize there was a construct called a function in bash. guess I spend too much time living with perl
-mark
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Karmic - bash globbing gone .... ??? syg00 Ubuntu 1 11-05-2009 07:25 AM
Globbing using Korn Shell metallica1973 Programming 1 12-05-2007 05:56 PM
LXer: CLI Magic: Using command history in the bash shell LXer Syndicated Linux News 0 07-03-2006 01:54 PM
Bash shell script - variable magic needed (no idea how to approach this) arrenlex Programming 8 03-02-2006 06:41 PM
Globbing Problem in BASH Hink Programming 2 07-28-2005 01:19 PM

LinuxQuestions.org > Forums > Linux Forums > Linux - Software

All times are GMT -5. The time now is 06:25 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration