Dynamically pipe user input into a variable for ls bash - bash

I have the following one sed liner that I use to prettify the output of lsing all my .desktop files.
ls -1 | sed -e 's/\.desktop$//' | sed -e 's/\org.gnome.//' | grep "$name" | head -1
Currently I have a read command that I pipe into that $name variable for input. Can you give me some ideas on how to make it dynamically output or autocomplete what im typing as app launchers like Rofi or others do?

You could try the peco utility. (It's also packaged for several distributions.)
From its github page:
peco can be a great tool to filter stuff like logs, process stats, find files, because unlike grep, you can type as you think and look through the current results.
The fzf "commandline fuzzy finder" utility (repos) is similar:
It's an interactive Unix filter for command-line that can be used with any list; files, command history, processes, hostnames, bookmarks, git commits, etc.
There's a list of other options at: https://alternativeto.net/software/peco/

Related

Shell script not working while writing the same on the Terminal works [duplicate]

I have a simple one-liner that works perfectly in the terminal:
history | sort -k2 | uniq -c --skip-fields=1 | sort -r -g | head
What it does: Gives out the 10 most frequently used commands by the user recently. (Don't ask me why I would want to achieve such a thing)
I fire up an editor and type the same with a #!/bin/bash in the beginning:
#!/bin/bash
history | sort -k2 | uniq -c --skip-fields=1 | sort -r -g | head
And say I save it as script.sh. Then when I go to the same terminal, type bash script.sh and hit Enter, nothing happens.
What I have tried so far: Googling. Many people have similar pains but they got resolved by a sudo su or adding/removing spaces. None of this worked for me. Any idea where I might be going wrong?
Edit:
I would want to do this from the terminal itself. The system on which this script would run may or may not provide permissions to change files in the home folder.
Another question as suggested by BryceAtNetwork23, what is so special about the history command that prevents us from executing it?
Looking at your history only makes sense in an interactive shell. Make that command a function instead of a standalone script. In your ~/.bashrc, put
popular_history() {
history | sort -k2 | uniq -c --skip-fields=1 | sort -r -g | head
}
To use history from a non-interactive shell, you need to enable it; it is only on by default for interactive shells. You can add the following line to the shell script:
set -o history
It still appears that only interactive shells will read the default history file by, well, default, so you'll need to populate the history list explicitly with the next line:
history -r ~/.bash_history
(Read the bash man page for more information on using a file other than the default .bash_history.)
History command is disabled by default on bash script, that's why even
history command won't work in .sh file. for its redirection. Kindly
redirect bash_history file inside the .sh file.
History mechanism can be enabled also by mentioning history file and change run-time parameters as mentioned below
#!/bin/bash
HISTFILE=~/.bash_history
set -o history
Note: mentioned above two lines on the top of the script file. Now history command will work in history.

How to not lose color when pipe output to variable [duplicate]

If I do
$ ls -l --color=always
I get a list of files inside the directory with some nice colouring for different file types etc..
Now, I want to be able to pipe the coloured output of ls through grep to filter out some files I don't need. The key is that I still want to preserve the colouring after the grep filter.
$ ls -l --color=always | grep -E some_regex
^ I lose the colouring after grep
EDIT: I'm using headless-server Ubuntu 8.10, Bash 3.2.39, pretty much a stock install with no fancy configs
Your grep is probably removing ls's color codes because it has its own coloring turned on.
You "could" do this:
ls -l --color=always | grep --color=never pattern
However, it is very important that you understand what exactly you're grepping here. Not only is grepping ls unnecessary (use a glob instead), this particular case is grepping through not only filenames and file stats, but also through the color codes added by ls!
The real answer to your question is: Don't grep it. There is never a need to pipe ls into anything or capture its output. ls is only intended for human interpretation (eg. to look at in an interactive shell only, and for this purpose it is extremely handy, of course). As mentioned before, you can filter what files ls enumerates by using globs:
ls -l *.txt # Show all files with filenames ending with `.txt'.
ls -l !(foo).txt # Show all files with filenames that end on `.txt' but aren't `foo.txt'. (This requires `shopt -s extglob` to be on, you can put it in ~/.bashrc)
I highly recommend you read these two excellent documents on the matter:
Explanation of the badness of parsing ls: http://mywiki.wooledge.org/ParsingLs
The power of globs: http://mywiki.wooledge.org/glob
You should check if you are really using the "real" ls, just by directly calling the binary:
/bin/ls ....
Because: The code you described really should work, unless ls ignores --color=always for some weird reason or bug.
I suspect some alias or function that adds (directly or through a variable) some options. Double-check that this isn't the case.

the command wc's function

I encouter many shell script in these days using command wc which is really awesome.For example:
For example, in the last 20 hours or so, 16 new users registered with FAS:
$ cat messages | grep \
'fedora-infrastructure:org.fedoraproject.prod.fas.user.create'|wc -l
16 /*output*/
calculate the lines of code in the . directory
find . -type f -exec cat '{}' \; | wc -l
I'm a newbie to Linux,so I want to now what kind of amazing stuff wc can do(with other commands support) not only basic usage come from man page
(Edit: It sounds like you might benefit most from understanding what pipes are, and how they work)
Try this on your command line:
man wc
It gives you the manual page for wc, with which you can find out all the things you can do with the tool.
And here are a few basic idioms / mental models to know when you are starting out learning Linux:
wc is one tool. There are many tools you can directly interact with in your [shell][2], such as: ls (lists contents of current directory), pwd (prints your current working directory), date (prints the current time), and more advanced ones such as awk, sed, grep, tr.
The | syntax (but not ||!) is called a "pipe":
By default, commands can either read from stdin (standard input) or read from file, or don't need to read anything at all (ls, for example, doesn't require input)
So when you do something like ls | wc -l, it's actually saying:
run ls
take the output of ls, which would normally be written to stdout (standard output), and "pipe it" directly into the stdin of wc, which, together with the -l option to wc, counts the number of lines.
There is no exhaustive list of other commands that wc can interact with, due to the pipes-and-filters paradigm in shell languages. Anything you see something like ... | wc, it just means that whatever output by the program before the | is fed to wc as input.
Unix tools are designed to be very simple, things like wc are very good examples of this.
wc = Word count
wc -l = Line count
In unix you can direct output from one command to another using |
The idea is that you combine these small tools together to achieve results way beyond their individual capacities.
find
Is a very powerful command with many options, but essentially finds filenames for files matching the predicates and options specified.
man
man is a help system built into unix, just type man wc to get info about wc you can use it for most commands available from the command line, with only a few exceptions.
Zsh, Antigen & Oh-My-Zsh
Unix is a lot easier with a good shell, and helpful tools. I recommend you use Zsh, the easy way to get this setup is to use Antigen and Oh-my-zsh (Antigen will help install Oh-My-Zsh and a bunch of other tools, so follow it's instructions.)
Once you have it setup, you have Tab auto-completion, showing commmands, command options (for many tools, such as git, find etc... etc..)
This will very quickly transform the murky darkness of the shell into a vibrant environment to work in.
How to learn cool combinations of commands?
Well, to get started, always remember you have basic looping and conditions available on the unix shell.
loops
while
for
until
select
conditions
if
case
These commands usually work with filename patterns, e.g. *.txt and also often work with piped | input.
For example, if you wanted to perform a complex operation, let's say rename a set of files replacing a given pattern:
for f in *; mv $a ${a/bak/}
Would remove the word bak from all the filenames in the current folder.\
Hidden gold
There are two commands sed and awk which are almost languages in their own right. It is not necessary to know them inside out, and many things they do can be replicated by simpler commands, such as tr and cut. However, understanding how they basically work is a very handy thing to know. In fact, Sed and Awk are so powerful, they even have an O'Reilly book dedicated to them.
Where to find examples of Unix command line awesomeness?
A good place to look for examples is command-line fu
Good luck

Bash: piped argument to open command fails. Open commands excutes too early?

I'm pretty much a novice to shell scripting. I'm trying to send the output of some piped commands to an open command in bash in OSX.
My ultimate goal is to compile a Flex/Actionscript application from TextWrangler by calling a bash script with a little Applescript and have the result played directly in a Flash Player. The Applescript is pretty much doing it's job. But the bash script doesn't work as I expect. Same results when I ommit the Applescript and simply put it directly in terminal.
This is what the Applescript is sending to terminal:
mxmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//' | open -a 'Flash Player'
So basically, I read the last line of the output of mxmlc, which usually looks something like this:
/Users/fireeyedboy/Desktop/DocumentClass.swf (994 bytes)
and I strip everything after the first space it encounters. I know it's hardly bulletproof yet, it's still just a proof of concept. When I get this roughly working I'll refine. It returns the desired result so far:
/Users/fireeyedboy/Desktop/DocumentClass.swf
But as you can see, I then try to pipe this sed result to the Flash Player and that's where it fails. The Flash Player seems to open way too early. I would expect the Flash Player to open only after the script finished the sed command. But it opens way earlier.
So my question is twofold:
Is it even possible to pipe an
argument to the open command this
way?
Do I need to use some type
of delay command to get this
working, since the open command doesn't seem to be waiting for the input?
You're trying to give the name of the swf file as input to stdin of the open command, which it doesn't support.
It expects the file name as an argument (similar to -a).
You can do something like this:
FILENAME=`xmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//'`
open -a 'Flash Player' $FILENAME
or on a single line:
open -a 'Flash Player' `xmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//'`
If you're using bash (or another modern POSIX shell), you can replace the pretty unreadable backtick character with $( and ):
open -a 'Flash Player' $(xmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//')
All commands in a pipe are started at the same time. During this step, their input/outputs are chained together.
My guess is that open -a 'Flash Player' doesn't wait for input but simply starts the flash player. I suggest to try to run the player with an argument instead:
name=$(mxmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//')
open -a 'Flash Player' "$name"
I'm not familiar with the "open" command as it seems to be a mac thing, but i think what you want to do is:
open -a 'Flash Player' $(mxmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//')
In general you can't pipe arguments to a command, you have to specify that you want the output of the previous command to be treated as arguments, either as in my example or with the xargs command. Note that there is a limit on the maximum size of a command line, though.

To understand xargs better

I want to understand the use of xargs man in Rampion's code:
screen -t man /bin/sh -c 'xargs man || read'
Thanks to Rampion: we do not need cat!
Why do we need xargs in the command?
I understand the xargs -part as follows
cat nothing to xargs
xargs makes a list of man -commands
I have had an idea that xargs makes a list of commands. For instance,
find . -type f -print0 | xargs -0 grep masi
is the same as a list of commands:
find fileA AND grep masi in it
find fileB AND grep masi in it
and so on for fileC, fileD, ...
No, I don't cat nothing. I cat whatever input I get after I run the command. cat is actually extraneous here, so let's ignore it.
xargs man waits on user input. Which is necessary. Since in the script you grabbed that from, I can't paste in the argument for man until after I create the window. So the command that runs in the window needs to wait for me to give it something, before it tries to run man.
If we just ran screen /bin/sh -d 'man || read', it would always complain "What manual page do you want?" since we never told it.
xargs gathers arguments from stdin and executes the command given with those arguments.
so cat is waiting for something to be typed, and then xargs is running man with that input.
xargs is useful if you have a lot of files to process, I often use it with output from find.
xargs will stuff as many arguments as it can onto the command line.
It's great for doing something like
find . -name '*.o' -print | xargs rm
The cat command does not operate on nothing; it operates on standard input, up until it is told that the input is ended. As Rampion notes, the cat command is not necessary here, but it is operating on its implicit input (standard input), not on nothing.
The xargs command reads the output from cat, and groups the information into arguments to the man command specified as its (only) argument. When it reaches a limit (configurable on the command line), it will execute the man command.
The find ... -print0 | xargs -0 ... idiom deals with file names that contain awkward characters such as blanks, tabs and newlines. The find command prints each filename followed by an ASCII NUL ('\0'); this is one of two characters that cannot appear in a simple file name - the other being '/' (which appears in path names, of course, but not in simple file names). It is not directly equivalent to the sequence you provide; xargs groups collections of file names into a single argument list, up to a size limit. If the names are short enough (they usually are), then there will be fewer executions of grep than there are file names.
Note, too, the grep only prints the file name where the material is found if it has more than one file to search -- or if it supports an option so that it always prints the file names and the option is used: '-H' is a GNU extension to grep that does this. The portable way to ensure that the file names always appear is to list /dev/null as the first file (so 'xargs grep something /dev/null'); it doesn't take long to search /dev/null.

Resources