Moreutils: A collection of Unix tools that nobody thought to write long ago
source link: https://news.ycombinator.com/item?id=31043655
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Moreutils: A collection of Unix tools that nobody thought to write long ago
awk '{do_stuff()}' myfile.txt | sort -u | column --table > myfile.txt
The shell will truncate myfile.txt before awk (or whatever command) has a chance to read it. So you can use sponge instead, which waits until it reads EOF to truncate/write the file. awk '{do_stuff()}' myfile.txt | sort -u | column --table | sponge myfile.txt
Personally, no. I prefer to output text processsing results to a different file, maybe a temporary one, then replace the first file with the second file after looking at the second file.
awk '{do_stuff()}' 1.txt | sort -u | column --table > 2.txt
less 2.txt
mv 2.txt 1.txt
With this sponge program I cannot check the second file before replacing the first one. The only reason I can think of not to use a second file is if I did not have enough space for the second file. In that case, I would use a fifo as the second file. (I would still look at the fifo first before overwriting the first file.) mkfifo 1.fifo
awk '{do_stuff()}' 1.txt | sort -u | column --table > 1.fifo &
less -f 1.fifo
awk '{do_stuff()}' 1.txt | sort -u | column --table > 1.fifo &
cat 1.fifo > 1.txt
I used sponge a lot in scripts where I’ve already checked and validated its behavior. When I’m confident it works I can keep just use sponge for simplicity.
Speaking from (bad) experience
#!/bin/sh
test $# = 1||exec echo usage: $0 file;
test -f $1||exec echo $1 does not exist;
test ! -f $1.tmp||exec echo $1.tmp exists;
cat > $1.tmp;
mv $1.tmp $1;
Just was intrigued, googled it, read about it and know it solves a problem I had elegantly. Some new tool in my belt from now on.
Thank you.
While you're at it, may as well create a backup of the $1 file.
Never have I ever needed -f.
Is the work of programmers less valuable than, say, the work of Google Docs users (where there is an undo operation)?
I agree it would be nice to have this in the filesystem; some filesystems support this (e.g. NILFS[1]), but none of the current "mainstream" ones do AFAIK. In the meanwhile, git works well enough.
Mac has Time Machine.
Personally I’d be interested in a shell having undo capability, but not a file system.
I'm not sure how these sentences are connected. Are you implying that allowing undo would make those problems significantly worse? I'm not sure of that. If you have a CoW filesystem, which you probably want for other reasons, then having a continuous snapshot mode for recent activity would not need much overhead.
If you're saying there's too much activity to allow an undo, well, I assume the undo would be scoped to specific files or directories!
In my case I could recreate the original file but it took 90 minutes to scrape the remote API to do it again...
Use a temporary file always. Sponge process may be interrupted, and you end up with a half-complete /etc/passwd in return.
rename is an atomic operation from any modern filesystem's perspective, you're not writing new data, you're simply changing the name of the existing file, it either succeeds or fails.
Keep in mind that if you're doing this, mv (the command line tool) as opposed to the `rename` system call, falls back to copying if the source and destination files are on different filesystems since you can not really mv a file across filesystems!
In order to have truly atomic writes you need to:
open a new file on the same filesystem as your destination file
write contents
call fsync
call rename
call sync (if you care about the file rename itself never being reverted).
This is some very naive golang code (from when I barely knew golang) for doing this which has been running in production since I wrote it without a single issue: https://github.com/AdamJacobMuller/atomicxt/blob/master/file...
Are those for networked like NTFS or just as security against crashes.
Logically on a single system there would be no effect assuming error free filesystem operation. Unless I'm missing something.
ext4 on Linux (since 2009) special-cases rename() when overwriting an existing file so that it works safely even without fsync() (https://lwn.net/Articles/322823/), but that is not guaranteed by all other implementations and filesystems.
The sync() at the end is indeed not needed for the atomicity, it just allows you to know that after its completion the rename will not "roll back" anymore on a crash. IIRC you can also use fsync() on the parent directory to achieve this, avoiding sync() / syncfs().
This is interesting.
The linked git entry (https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.g...) from the LWN article says "Notice: this object is not reachable from any branch."
Did this never get merged because I definitely saw this issue in production well after 2009.
I guess it either got changed, or, a different patch applied but perhaps this https://github.com/torvalds/linux/blob/master/fs/ext4/namei.... does it?
And yes, the code you highlighted is exactly this special-case in its current form. The mount option "noauto_da_alloc" can be used to disable these software-not-calling-fsync safety features.
The final sync is just there for durability, not atomicity, like you say.
Correct.
The rename can succeed while the write of the file you just renamed gets rolled back.
1. Write to a temporary file 2. Do the equivalent of mv tmpfile originalfile
so it will either succeed or do nothing
cmd < somefile | somefile.tmp && mv somefile.tmp somefile
Will read from somefile, and only replace the source if (and when) the pipleline exits successfully.Mind that this may still bite in interesting ways. But less frequently.
You can also invoke tempfile(1) which is strongly recommended in scripts.
> tempfile is deprecated; you should use mktemp(1) instead.
git init .
git add .
git commit -m "wip"
... and proceed from there. So many ways to screw up ad hoc data processing using shell and the above can be a life saver. (Along with committing along the way, ofc.)EDIT: Doesn't work if you have huuuuge files, obviously... but you should perhaps be using different tools for that anyway.
Sure there could be some situations where it could be handy, like in some auomated scenarios, but most of the time it is not a big deal to write
foo data1 data2
bar data2
Now you have to use `tee` for that, that is fine but if you don't want to echo the file back to the terminal you have to do
command | sudo tee file > /dev/null
with this you can simply do command | sudo sponge file
I seem to get this wrong roughly once a week.
cat myfile.txt | awk '{do_stuff()}' | sort -u | column --table > new-myfile.txt
awk '{do_stuff()}' myfile.txt | sort -u | column --table > new-myfile.txt
The only thing you’ve changed is that you’re sending the output to a new file. That’s fine, but it’s what sponge is avoiding.
Isn’t there already a project to bring Rust to the shell?
this is an artifact of how handles work (in relation to concurrency), not the filesystem.
copy-on-write still guarantees a consistent view of the data, so if you write on one handle you're going to clobber the data on the other, because that's what's in the file.
what you really want is an operator which says "I want this handle to point to the original snapshot of this data even if it's changed in the meantime", which a CoW filesystem could do, but you'd need some additional semantics here (different access-mode flag?) which isn't trivially granted just by using a CoW filesystem underneath.
When I found `sponge`, I couldn't help but wonder where it had been all of my life. It's nice to be able to modify an in-place file with something other than `sed`.
while : ; do
( tput reset
run-slow-command ) | tac | tac
done
awk '{do_stuff()}' myfile.txt | sort -u | column --table | sponge > myfile.txt
(cat > $OUT.tmp; mv -f $OUT.tmp $OUT)
Hmmm ... "When possible, sponge creates or updates the output file atomically by renaming a temp file into place. (This cannot be done if TMPDIR is not in the same filesystem.)"
My shell fragment already beats sponge on this feature!
awk 'BEGIN{system("rm myfile.txt")}{do_stuff()}' <myfile.txt | sort -u | column --table > myfile.txt
In general though: <foo { rm -f foo && wc >foo }
I don't think overwriting the input data is a good idea due to risk of data loss.
Yes I should have done a bit more homework but shaming for asking a clarifying question is unreasonable. Those of you who have the downvote trigger-finger can and should do better.
The same principle works the other way too - when a comment doesn't contain much information, readers tend to interpret it according to whatever they personally find the most irritating, annoying, or hurtful, and then react to that. Our minds are not our best friends this way.
The (partial) solution to this is to include enough disambiguating information in your comment. For example if your comment had contained enough information to make clear that your question was genuinely curious rather than snarkily dismissive, I doubt it would have gotten downvoted.
It's hard to do that because generally our intention is so clear and transparent to ourselves that it doesn't occur to us to include it in the message. Unfortunately for all of us on the internet, however, intent doesn't communicate itself.
tee actually does sorta work for this sometimes, but it’s not guaranteed to wait until EOF. For example I tested with a 10 line file where I ran `sort -u file.txt | tee file.txt` and it worked fine. But I then tried a large json file `jq . large.json | tee large.json` and the file was truncated before jq finished reading it.
> vipe: insert a text editor into a pipe
It's useful when you want to edit some input text before passing it to a different function. For example, if I want to delete many of my git branches (but not all my git branches):
$ git branch | vipe | xargs git branch -D
`vipe` will let me remove some of the branch names from the list, before they get passed to the delete command.
More to it, not in a pipe (because of poor ^C behavior), but using a hokey in zsh to bring up git branch | fzf, select any number of branches I need and put them on command line, this is extremely composable.
Also, you can construct your pipeline so that a blank file (or some sentinel value) returned from vipe means “abort”. A good example of this is when git opens your editor for interactive merging — deleting all lines cancels the whole thing.
But maybe not. I haven't tried it yet (and it does seem really useful).
You could kill them before exiting the editor, if that's what you want. Or you could do something else.
The other commands in the pipeline are run by the parent shell, not vipe, so handling this would not be vipe specific.
I often use it with tee to save the log output of any command.
$ ping google.com | ts '[%Y%m%d-%H:%M:%.S]' | tee /tmp/ping.log
[20220416-21:57:20.837983] PING google.com (172.217.175.78): 56 data bytes
[20220416-21:57:20.838391] 64 bytes from 172.217.175.78: icmp_seq=0 ttl=53 time=6.028 ms
[20220416-21:57:21.817189] 64 bytes from 172.217.175.78: icmp_seq=1 ttl=53 time=9.621 ms
[20220416-21:57:22.818339] 64 bytes from 172.217.175.78: icmp_seq=2 ttl=53 time=9.443 ms
[20220416-21:57:23.823126] 64 bytes from 172.217.175.78: icmp_seq=3 ttl=53 time=8.921 ms
I like this. Reminds me of a couple very useful things I've done:
1. Add a -man switch to command line programs. This causes a browser to be opened on the web page for the program. For example:
dmd -man
opens https://dlang.org/dmd-windows.html in your default browser.2. Fix my text editor to recognize URLs, and when clicking on the URL, open a browser on it. This silly little thing is amazingly useful. I used to keep bookmarks in an html file which I would bring up in a browser and then click on the bookmarks. It's so much easier to just put them in a plain text file as plain text. I also use it for source code, for example the header for code files starts with:
/*
* Takes a token stream from the lexer, and parses it into an abstract syntax tree.
*
* Specification: $(LINK2 https://dlang.org/spec/grammar.html, D Grammar)
*
* Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/parse.d, _parse.d)
* Documentation: https://dlang.org/phobos/dmd_parse.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parse.d
*/
and I'll also use URLs in the source code to reference the spec on what the code is implementing, and to refer to closed bug reports that the code fixes.Very, very handy!
For contrast, there's this site:
https://www.felixcloutier.com/x86/index.html
which is a godsend to me. Now, in the dmd code generator, I put in links to the detail page for an instruction when the code generator is generating that instruction. Oh, how marvelous that is! And there is joy in Mudville.
Also uops.info is a good reference for how fast the instructions are
ts timestamps each line of the input, which I've found convenient for ad-hoc first-pass profiling in combination with verbose print statements in code: the timestamps make it easy to see where long delays occur.
errno is a great reference tool, to look up error numbers by name or error names by number. I use this for two purposes. First, for debugging again, when you get an errno numerically and want to know which one it was. And second, to search for the right errno code to return, in the list shown by errno -l.
And finally, vipe is convenient for quick one-off pipelines, where you know you need to tweak the input at one point in the pipeline but you know the nature of the tweak would take less time to do in an editor than to write the appropriate tool invocation to do it.
On linux, errno.h is fragmented across several places because errnos are different on different architectures. I think this started because when Linus did the initial alpha port, he bootstrapped Linux using a DEC OSF/1 userland, which meant that he had to use the DEC OSF/1 BSD-derived values of errno rather than the native linux ones so that they would run properly. I'm not sure why this wasn't cleaned up before it made it into the linux API on alpha.
At least on FreeBSD, determining what errno means what is just a grep in /usr/include/sys/errno.h. And it uses different errnos for different ABIs (eg, linux binaries get their normal errnos, not the FreeBSD ones).
errno () {
grep \\\<$*\\\> /usr/include/sys/errno.h
}
There will be a false positive now & then but this is good enough.
Like, none of the everyday tools I use produce exit codes which correspond to these explanations.
For my scripts, I just return 1 for generic erros and 2 for bad usage. I wished I could be more specific in that and adhered to some standard.
There's no standard for process exit codes, other than "zero for success, non-zero for error".
https://sourceware.org/git/?p=glibc.git;a=blob;f=misc/sysexi...
See here for why you can’t use an exit status of 128+signal to fake a “killed by signal” state, either:
Well, Joey started moreutils in 2006, so it's more than half way to that original "30 years ago" threshold!
[1]: http://source.joeyh.branchable.com/?p=source.git;a=blob;f=mo...
$ pbpaste | vipe | pbcopy
Which will open your editor so you can edit whatever is in your clipboard.
I don't think it's a good idea to rely on any of these being present. If you write a shell script to share and expect them to be there, you aren't being friendly to others, but for interactive command line use, I'm happy to adopt new tools.
Ditto. But I will probably forget they exist and go do the same old silly kludges with subshells and redirections. May I ask if anyone has a technique for introducing new CL tools into their everyday workflow?
It helps if things have good man, apropos and help responses, but the problem is not how new tools function, rather remembering that they exist at all.
Sometimes I think I want a kind of terminal "clippy" that says:
"Looks like you're trying to match using regular expressions, would you like me to fzf that for you?"
Then again, maybe not.
That being said, I’ve been meaning to add a Linux and Mac install.sh script to my dotfiles repo for installing all my CLI tools, and that could probably serve as a good reminder of all the tools you’ve come across over the years that might provide some value.
Pick one tool a year. Write it on a sticky note on your monitor. Every time you're doing something on the command line, ask yourself "would $TOOL be useful here?".
You're not going to have high throughput on learning new tools this way, but it'll be pretty effective for the tools you do learn.
Isn't that a shame though? Where does it say in the UNIX philosophy that the canon should be closed?
There are shades of grey, of course. Bash is probably ubiquitous enough that it may not be a big issue if a script that's meant to be universal depends on it, as long as the script explicitly specifies bash in the shebang. Sometimes some particular functionality is not technically part of a standard but is widely enough supported in practice. Sometimes the standards (either formal or de facto) are expanded to include new functionality, and that's of course totally fine, but it's not likely to be a very quick process because there are almost certainly going to be differing opinions on what should be part of the core and what shouldn't.
Either way, sometimes you want to write for the lowest common denominator, and moreutils certainly aren't common enough that they could be considered part of that.
pee: tee standard input to pipes
sponge: soak up standard input and write to a file
ts: timestamp standard input
vidir: edit a directory in your text editor
vipe: insert a text editor into a pipe
zrun: automatically uncompress arguments to command
Wth do they do? What does any of this means? I know tee but have no idea what « tee stdin to pipes » would do
`sponge` allows a command to overwrite a file in-place; e.g. if we want to replace the contents of myfile.txt, to only keep lines containing 'hello', we might try this:
grep 'hello' < myfile.txt > myfile.txt
However, this won't work: as soon as grep finds the first matching line, the entire contents of the file will be overwritten, preventing any more from being found. The `sponge` command waits until its stdin gets closed, before writing any output: grep 'hello' < myfile.txt | sponge > myfile.txt
From your description I guess something like « pee a b » would be the same as « a | b »? If so that’s cool, but the one line descriptions definitely need a rework.
$ seq 3 | tee somefile
1
2
3
╔═somefile
seq═tee═╣
╚═stdout
$ seq 10 | pee 'head -n 3' 'tail -n 3'
1
2
3
8
9
10
╔═head═╗
seq═pee═╣ ╠═stdout
╚═tail═╝
I think Zsh supports this natively with its "multios" option, `a > >(b c y z) > >(c)`. But then you have to write the rest of your pipe inside the ().
I once had to use an IBM mainframe shell(cms If I remember correctly) which had pipes however IBM in their infinite wisdom decided to make the pipe network the primary interface, while this made complex pipes a bit less awkward, simple(single output) pipes were wordy compared to the unix equivalent.
chronic runs a command, and arranges for its standard out and standard error to only be displayed if the command fails (exits nonzero or crashes). If the command succeeds, any extraneous output will be hidden.
A common use for chronic is for running a cron job. Rather than trying to keep the command quiet, and having to deal with mails containing accidental output when it succeeds, and not verbose enough output when it fails, you can just run it verbosely always, and use chronic to hide the successful output.
combine combines the lines in two files. Depending on the boolean operation specified, the contents will be combined in different ways:
and Outputs lines that are in file1 if they are also present in file2.
not Outputs lines that are in file1 but not in file2.
or Outputs lines that are in file1 or file2.
xor Outputs lines that are in either file1 or file2, but not in both files.
The input files need not be sorted
ifdata can be used to check for the existence of a network interface, or to get information about the interface, such as its IP address. Unlike ifconfig or ip, ifdata has simple to parse output that is designed to be easily used by a shell script.
lckdo: Now that util-linux contains a similar command named flock, lckdo is deprecated, and will be removed from some future version of moreutils.
mispipe: mispipe pipes two commands together like the shell does, but unlike piping in the shell, which returns the exit status of the last command; when using mispipe, the exit status of the first command is returned.
Note that some shells, notably bash, do offer a pipefail option, however, that option does not behave the same since it makes a failure of any command in the pipeline be returned, not just the exit status of the first.
pee: [my own description: `pee cmd1 cmd2 cmd3` takes the data from the standard input, sends copies of it to the commands cmd1, cmd2, and cmd3 (as their stdin), aggregates their outputs and provides that at the standard output.]
sponge, ts and vipe have been described in other comments in this thread. (And I've also skipped some easier-to-understand ones like errno and isutf8 for the sake of length.)
zrun: Prefixing a shell command with "zrun" causes any compressed files that are arguments of the command to be transparently uncompressed to temp files (not pipes) and the uncompressed files fed to the command.
The following compression types are supported: gz bz2 Z xz lzma lzo
[One super cool thing the man page mentions is that if you create a link named z<programname> eg. zsed, with zrun as the link target, then when you run `zsed XYZ`, zrun will read its own program name, and execute 'zrun sed XYZ' automatically.]
That's a brilliant way to improve the ergonomics.
Or just use an alias
# Launch $EDITOR to let you edit your env vars.
function viset() {
if [ -z "$1" ]; then
echo "USAGE: viset THE_ENV_VAR"
exit 1
else
declare -n ref=$1
f=$(mktemp)
echo ${!1} > $f
$EDITOR $f
ref=`cat $f`
export $1
fi
}
# Like viset, but breaks up the var on : first,
# then puts it back together after you're done editing.
# Defaults to editing PATH.
#
# TODO: Accept a -d/--delimiter option to use something besides :.
function vipath() {
varname="${1:-PATH}"
declare -n ref=$varname
f=$(mktemp)
echo ${!varname} | tr : "\n" > $f
$EDITOR $f
ref=`tr "\n" : < $f`
export $varname
}
Mostly I use vipath because I'm too lazy to figure out why tmux makes rvm so angry. . . .I guess a cool addition to viset would be to accept more than one envvar, and show them on multiple lines. Maybe even let you edit your entire env if you give it zero args. Having autocomplete-on-tab for viset would be cool too. Maybe even let it interpret globs so you can say `viset AWS*`.
Btw I notice I'm not checking for an empty $EDITOR. That seems like it could be a problem somewhere.
ched also looks quite useful; automatically cleaning the old data after some time is great, as I commonly leave it lying around.
age also looks great for processing recent incoming files in a large directory (my downloads, for example)
p looks great to me, I rarely need the more advanced features of parallel, and will happily trade them for color coded outputs.
I looked to see what nup is because I don't understand the description...only to find out it doesn't actually exist. I'm assuming it's intended to send a signal to a process? But if so, why not just use `kill -s sigstop`?
pad also doesn't exist, but seems like printf or column could replace it, as these are what I usually use. I think there's also a way to pad variables in bash/zsh/etc.
whl is literally just `while do_stuff; do; done` and repeat is just `while true; do do_stuff; done`. It never even occurred to me to look for a tool to do untl; I usually just use something along the lines of `while ! do_stuff; do c=$((c + 1)); echo $c; done`. While the interval and return codes make it almost worthwhile, they themselves are still very little complexity; parsing the parameters adds more complexity than their implementation does.
spongif seems useful, but is really just a variation of the command above.
Name it at least c-parallel
I’m using Arch, btw.
In fact, I take it back. ifdata(1) is not in any way a replacement for ifconfig(1) for most things. The problem is that just running ifconfig with no arguments showed you everything, which was generally perfect for interactive use. Now to get any information from ip(1) you have to remember an argument name. If you do this a lot, it's almost certainly fine. If you do it occasionally, it's horrible.
Although I do wish MacOS had a fully-compatible ip command instead of ifconfig et al.
One does have to wonder though, why isn't -brief the default and the current default set to -verbose or -long. I look at -brief on either command and it has all the information I am ever looking for.
https://github.com/johnkerl/miller
"Miller is like awk, sed, cut, join, and sort for name-indexed data such as CSV, TSV, and tabular JSON."
This should have been part of the standard unix toolkit for the last 40 years.
I know that I could obviously grep the output of the first command, and then use sed or awk to manipulate the line I want to get just the url, but I'm not sure about the best way to go about the rest. In addition, I usually want to see all the output of the first command (in this case, it's not done executing, it continues to run after printing out the url), so maybe there's a way to do that with tee? But I usually ALSO don't want to intermix 2 commands in the same shell, i.e. I don't want to just have a big series of pipes, Ideally I could run the 2 commands separately in their own terminals but the 2nd command that needs the url would effectively block until it received the url output from the first command. I have a feeling maybe you could do this with named pipes or something but that's pretty far out of my league...would love to hear if this is something other folks have done or have a need for.
$ mkfifo myfifo
$ while true; do sed -rune 's/^Dev server started on port (.*)/\1/p' myfifo | xargs -n1 -I{} echo "Execute other command here with argument {}"; done
In the other terminal, run your server and tee the output to the fifo you just created: $ start_server | tee myfifo
You can create a named pipe using "mkfifo", which creates a pipe "file" with the specified name. Then, you can tell your programs to read and write to the pipe the same way you'd tell them to read and write from a normal file. You can use "<" and ">" to redirect stdout/stderr, or you can pass the file name if it's a program that expects a file name.
$ wc -l miniexpect.[ch]
489 miniexpect.c
110 miniexpect.h
1. Run one command with output to a file, possibly in the background. Since you want to watch the output, run “tail --follow=name filename.log”.
2. In a second terminal, run a second tail --follow on the same log file but pipe the output to a command sequence to find and extract the URL, and then pipe that into a shell while loop; something like “while read -r url; do do-thing-with "$url"; done”.
… good luck, is my best advice. It’s not straightforward to handle edge cases.
The Debian BTS thread:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=749355
(I'm still trying to sort out what the resolution was, though it appears divert was used.)
moreutils has the following notes:
The binary parallel is no longer in this port; please install the port parallel instead.
i.e. GNU parallel
> conflict (for package managers)
See e.g. https://www.gnu.org/software/parallel/parallel_tutorial.html
If you mean moreutils parallel, yeah I agree it's not useful.
I think I could pick apart about half of these and show how they aren't needed,
and the showpiece example is particularly weak since sed can do that do that entire pipeline itself in one shot, especially any version with -i.
You don't need either grep or sponge. Maybe sponge is still useful over simple shell redirection, but this example doesn't show it.
One of the other comments here suggests that the real point of sponge vs '>' is that it doesn't clobber the outoutput until the input is all read.
In that case maybe the problem is just that the description doesn't say anything about that. But even then there is still a problem, in that it should also stress that you must not unthinkingly do "sponge > file" because the > is done by the shell and not controlled by executable, and the shell may zero out the file immediately on parsing the line before any of the commands get to read it.
This makes sponge prone to unpleasant surprise because it leads the user to think it prevents something it actually has no power to prevent. The user still has to operate their shell correctly to get the result they want, just like without sponge.
So it's a footgun generator.
Maybe it's still actually useful enough to be worth writing and existing, but just needs some better example to show what the point is.
To me though, from what is shown, it just looks like an even worse example of the "useless use of cat" award, where you not only use cat for no reason, you also write a new cat for no reason and then use it for no reason.
But there is still something here. Some of these sound either good or at least near some track to being good.
I like it.
> chronic: runs a command quietly unless it fails
Isn't that just `command >/dev/null`?
> ifdata: get network interface info without parsing ifconfig output
`ip link show <if>`?
> isutf8: check if a file or standard input is utf-8
`file` for files. For stdin, when is it not utf8 - unless you've got some weird system configuration?
> lckdo: execute a program with a lock held
`flock`?
Often times you want to run a command silently (like in a build script), but if it fails with a nonzero exit status, you want to then display not only the stderr but the stdout as well. I’ve written makefile hacks in the past that do this to silence overly-chatty compilers where we don’t really care what it’s outputting unless it fails, in which case we want all the output. It would’ve been nice to have this tool at the time to avoid reinventing it.
> Isn't that just `command >/dev/null`?
no. that just shows your stderr, not stdout if it failed. and you get stderr even if it doesn't fail.
new ideas are great. but this isn't a new idea. "run a command silently unless it fails" is basic; it's the sort of thing that should make one think "i should search the shell man pages" rather than "i should roll my own new utility."
somecmd < somefile | othercmd | anothercmd > somefile
the output redirection will truncate the file before it can get read as input.Sponge "soaks up" all the output before writing any of it, so that you can write pipelines like that:
somecmd < somefile | othercmd | anothercmd | sponge somefile
$ cat foo
foo
bar
baz
$ ( rm foo && grep ba > foo ) < foo
$ cat foo
bar
baz
$
It has a basic understanding of common text file structures and also directories, which makes it super powerful.
0: I like this because it's much easier to edit IMHO
Nice tool, great name.
tee -p >(command | line)
Note the -p option available now with GNU tee that doesn't exit tee if the pipe closes early
Looking at the other comments, I suspect one of the difficulties in finding a new maintainer will be that lots of people use 2 or 3 commands from it, but nobody uses the same 2 or 3, and actually caring about all of them is a big stretch...
oops
https://github.com/figital/fstring
(just shows you more useful details about what is found)
sponge(1) soaks up input and writes it to the specified file at end of data.
tee doesn't sponge. sponge doesn't tee.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK