Coreutils stat have --format= switch which report different info about file (owner, size, etc) in easy form for readers.
POSIX ls utility provide most of this info, but its output is hard to parse. Compare with one-liner:
[ `stat -c '%U' $f` = $USER ] && echo "You own $f" || echo Error!
Are there stat utility analog in POSIX?
It is not possible :-(
Your options are:
Use ls and parse that with awk; the output of ls -l is in POSIX, so you
can rely on that. This works okay for some fields (such as the owner in your
example), and not so good for others (such as the mtime).
Detect the stat version and switch parameters; GNU stat has -c, BSD stat has
-f, other versions perhaps something else. stat is not in POSIX at all, and I don't know how widespread it is beyond Linux, BSD, and OSX, though.
Use a Perl or Python one-liner; this is not even remotely POSIX of course, but
making the assumption that at least one of these languages is present is a
fairly reasonable one in 2015, and it is easily detectable at startup if they are indeed present. It's also
not an option if performance is any concern.
Example, I've used mtime in all these examples, as this is difficult to get with ls:
#!/bin/sh
file="/etc/passwd"
perl -e "print((stat(\"$file\"))[9])"
echo
echo "$file" | perl -e '$i = <STDIN>; chomp($i); print((stat($i))[9])'
echo
python -c "import os; print(os.stat(\"$file\").st_mtime, end='')"
echo
echo "$file" | python -c "import os, sys; print(os.stat(sys.stdin.readline()[:-1]).st_mtime, end='')"
echo
I would recommend the Perl version; not because I like Perl, but because this
Python example only work correctly with Python 3 (specifically, the end=''
bit to prevent printing newlines.
A version to work with both Python 2 & 3 gets rather long:
python2 -c "from __future__ import print_function; import os; print(os.stat('/etc/passwd') .st_mtime, end='')"
You could also expand this with other languages (Ruby, PHP, Tcl, etc.), but Perl & Python are the most widespread by far.
Documentation for:
Perl stat(),
Perl lstat()
Python os.stat()
.
The short answer is no, POSIX does not provide a simple way to get the same output as stat. However, you can usually get relevant bits using other tools. To get the owner specifically:
ls -ld /path/of/file/or/directory | awk '{print $3}'
Related
just a quick question regarding a doubt. What's the difference between these two:
grep "$genre" "$i" | grep "$type" -c
and
grep "$genre" "$i" | grep -c "$type"
Do they do perhaps the same thing?
The POSIX standard-mandated behavior for grep "$type" -c, presuming that $type does not expand to a string starting with a dash, is to treat -c as a filename.
Only nonstandard versions of grep (such as the one built by the GNU project and commonly found on Linux systems) will treat -c as an option enabling "count" behavior when it isn't before the first positional argument.
It's bad practice to write your scripts to require nonstandard tools unless they gain some concrete benefit from those tools. Use grep -c "$type".
As you can see from man grep, the -c switch belongs to the so-called "General Output Control". Those switches can be placed on different places in the grep ... command and this has no impact on the general outcome, so indeed, both lines you mention are equal.
I saw there were several good answers for bash and even zsh(i.e. Here). Although I wasn't able to find a good one for fish.
Is there a canonical or clean one to prepend a string or a couple of lines into an existing file (in place)? Similar to what cat "new text" >> test.txt do for append.
As part of fish's intentional aim towards simplicity, it avoids syntactic sugar found in zsh. The equivalent to the zsh-only code <<< "to be prepended" < text.txt | sponge text.txt in fish is:
begin; echo "to be prepended"; cat test.txt; end | sponge test.txt
sponge is a tool from the moreutils package; the fish version requires it just as much as the zsh original did. However, you could replace it with a function easily enough; consider the below:
# note that this requires GNU chmod, though it works if you have it installed under a
# different name (f/e, installing "coreutils" on MacOS with nixpkgs, macports, etc),
# it tries to figure that out.
function copy_file_permissions -a srcfile destfile
if command -v coreutils &>/dev/null # works with Nixpkgs-installed coreutils on Mac
coreutils --coreutils-prog=chmod --reference=$srcfile -- $destfile
else if command -v gchmod &>/dev/null # works w/ Homebrew coreutils on Mac
gchmod --reference=$srcfile -- $destfile
else
# hope that just "chmod" is the GNU version, or --reference won't work
chmod --reference=$srcfile -- $destfile
end
end
function mysponge -a destname
set tempfile (mktemp -t $destname.XXXXXX)
if test -e $destname
copy_file_permissions $destname $tempfile
end
cat >$tempfile
mv -- $tempfile $destname
end
function prependString -a stringToPrepend outputName
begin
echo $stringToPrepend
cat -- $outputName
end | mysponge $outputName
end
prependString "First Line" out.txt
prependString "No I'm First" out.txt
For the specific case that file size is small to medium (fit in memory), consider using the ed program, which will avoid the temporary files by loading all data into memory. For example, using the following script. This approach avoid the need to install extra packages (moreutils, etc.).
#! /usr/env fish
function prepend
set t $argv[1]
set f $argv[2]
echo '0a\n$t\n.\nwq\n' | ed $f
end
I have this following handy little script that searches for aliases and bash functions. I'd like to extend it to bash autocomplete, i.e. find all the binaries on my PATH.
Come to think of it, the std autocomplete behavior would find the aliases and functions too. But if there is just a PATH binary list, that's good for me too.
i.e. how do I trigger the list-all-completions behavior in a bash function?
(venv) me#backups$ 👈 I entered a tab here
Display all 3093 possibilities? (y or n)
! libocijdbc12.dylib
./ libons.dylib
2to3 liboramysql12.dylib
2to3- libpng-config
.....
this is the bash script I currently use.
_getfilter(){
if [ -z "$1" ]; then
r_getfilter='.+'
else
r_getfilter="$1"
fi
}
findcommands(){
#hardcoding for now
#_getfilter $1
r_getfilter='^p'
printf "\nfunctions:\n"
declare -F | cut -c12- | egrep "$r_getfilter"
printf "\naliases:\n"
alias | cut -d '=' -f 1 | cut -d ' ' -f 2 | egrep "$r_getfilter"
printf "\nautocomplete:\n"
<what do I use here?> | egrep "$r_getfilter"
}
I would prefer not having to trawl individual directories in PATH for me-permissible executables. If that's the only solution, I would probably not bother.
the current findcommands implementation works on Linux and Mac. My priority is getting it to work on my Mac, Linux too is a nice-to-have.
Typing complete and compgen, which seem related, shows no output. and man has nothing much to say for either.
You're looking for compgen.
$ compgen -c
ffmpeg
la
ll
man
if
then
else
elif
fi
case
...
I am writing a bash script to take in some numbers output from a tool, they are numbers, truncated with the "e" notation (I can't remember the correct name for this).
So, it spits out numbers like 1.3684528004e+05 and 1.2815670938e+04.
How can I convert these into their full original number in my bash script; I have the usual binaries at my disposal such as bc and dc etc, this box also has php-cli installed and perl (Ubuntu 10.x).
Many thanks for reading.
You can use printf built-in:
$ x=1.3684528004e+05
$ printf "%f\n" $x
136845.280040
$ y=1.2815670938e+04
$ printf "%f\n" $y
12815.670938
I am, like many non-engineers or non-mathematicians who try writing algorithms, an intuitive. My exact psychological typology makes it quite difficult for me to learn anything serious like computers or math. Generally, I prefer audio, because I can engage my imagination more effectively in the learning process.
That said, I am trying to write a shell script that will help me master Linux. To that end, I copied and pasted a list of Linux commands from the O'Reilly website's index to the book Python In a Nutshell. I doubt they'll mind, and I thank them for providing it. These are the textfile `massivelistoflinuxcommands,' not included fully below in order to save space...
OK, now comes the fun part. How do I get this script to work?
#/bin/sh
read -d 'massivelistoflinuxcommands' commands <<EOF
accept
bison
bzcmp
bzdiff
bzgrep
bzip2
bzless
bzmore
c++
lastb
lastlog
strace
strfile
zmore
znew
EOF
for i in $commands
do
$i --help | less | cat > masterlinuxnow
text2wave masterlinuxnow -o ml.wav
done
It really helps when you include error messages or specific ways that something deviates from expected behavior.
However, your problem is here:
read -d 'massivelistoflinuxcommands' commands <<EOF
It should be:
read -d '' commands <<EOF
The delimiter to read causes it to stop at the first character it finds that matches the first character in the string, so it stops at "bzc" because the next character is "m" which matches the "m" at the beginning of "massive..."
Also, I have no idea what this is supposed to do:
$i --help | less | cat > masterlinuxnow
but it probably should be:
$i --help > masterlinuxnow
However, you should be able to pipe directly into text2wave and skip creating an intermediate file:
$i --help | text2wave -o ml.wav
Also, you may want to prevent each file from overwriting the previous one:
$i --help | text2wave -o ml-$i.wav
That will create files named like "ml-accept.wav" and "ml-bison.wav".
I would point out that if you're learning Linux commands, you should prioritize them by frequency of use and/or applicability to a beginner. For example, you probably won't be using bison right away`.
The first problem here is that not every command has a --help option!! In fact the very first command, accept, has no such option! A better approach might be executing man on each command since a manual page is more likely to exist for each of the commands. Thus change;
$i --help | less | cat > masterlinuxnow
to
man $i >> masterlinuxnow
note that it is essential you use the append output operator ">>" instead of the create output operator ">" in this loop. Using the create output operator will recreate the file "masterlinuxnow" on each iteration thus containing only the output of the last "man $i" processed.
you also need to worry about whether the command exists on your version of linux (many commands are not included in the standard distribution or may have different names). Thus you probably want something more like this where the -n in the head command should be replace by the number of lines you want, so if you want only the first 2 lines of the --help output you would replace -n with -2:
if [ $(which $i) ]
then
$i --help | head -n >> masterlinuxnow
fi
and instead of the read command, simply define the variable commands like so:
commands="
bison
bzcmp
bzdiff
bzgrep
bzip2
bzless
bzmore
c++
lastb
lastlog
strace
strfile
zmore
znew
"
Putting this all together, the following script works quite nicely:
commands="
bison
bzcmp
bzdiff
bzgrep
bzip2
bzless
bzmore
c++
lastb
lastlog
strace
strfile
zmore
znew
"
for i in $commands
do
if [ $(which $i) ]
then
$i --help | head -1 >> masterlinuxnow 2>/dev/null
fi
done
You're going to learn to use Linux by listening to help descriptions? I really think that's a bad idea.
Those help commands usually list every obscure option to a command, including many that you will never use-- especially as a beginner.
A guided tutorial or book would be much better. It would only present the commands and options that will be most useful. For example, that list of commands you gave has many that I don't know-- and I've been using Linux/Unix extensively for 10 years.