Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Edit files in place #105

Open
ghost opened this issue Apr 22, 2013 · 99 comments
Open

Edit files in place #105

ghost opened this issue Apr 22, 2013 · 99 comments

Comments

@ghost
Copy link

ghost commented Apr 22, 2013

With GNU Sed for example you can edit files in place:

-i[SUFFIX], --in-place[=SUFFIX]

       edit files in place (makes backup if SUFFIX supplied)

However I see no option for this with JQ, currently I must do this:

br=$(mktemp)
jq .charlie delta.json > "$br"
mv "$br" delta.json

Here is a workaround, Awk slurp:

jq .firstName alfa.json | awk 'BEGIN{RS="";getline<"-";print>ARGV[1]}' alfa.json
@karelv
Copy link

karelv commented Jan 17, 2014

+1

1 similar comment
@ipmcc
Copy link

ipmcc commented May 21, 2014

+1

@nicowilliams
Copy link
Contributor

Scripting around this is not an option? In shell:

tmpf=$(mktemp --tmpdir=$(dirname $f) -t)
jq ... "$f" > "$tmpf"
mv -f "$tmpf" "$f"

@nicowilliams
Copy link
Contributor

I'm asking why it's not acceptable.

@ipmcc
Copy link

ipmcc commented Jun 9, 2014

By analogy: Would it be acceptable if <insert your favorite word processor here> made you save to a different file every time you wanted to save your document?

It's not that the problem can't be worked around, it's that doing so is cumbersome. The fact that something as fundamentally stream oriented as sed has an in-place option is a pretty good indicator that this is common expectation/desire.

@nicowilliams
Copy link
Contributor

I see. I've generally not bothered with sed's -i option. For me the
problem has been two-fold: a) it is (well, was) not portable enough, b) I
always have to check whether such a tool's in-place update functionality is
documented as working this way (rename into place), because I generally
need atomicity. And then there's Windows (where renames generally don't
work when there's open file handles for the file in question, unless all
the processes that opened it used an option to allow it).

On Mon, Jun 9, 2014 at 10:23 AM, ipmcc [email protected] wrote:

By analogy: Would it be acceptable if made you save to a different file
every time you wanted to save your document?

It's not that the problem can't be worked around, it's that doing so is
cumbersome. The fact that something as stream oriented as sed has an
in-place option is a pretty good indicator that this is common
expectation/desire.


Reply to this email directly or view it on GitHub
#105 (comment).

@nicowilliams
Copy link
Contributor

Anyways, I can add this easily. My only concern is to limit the number of
new command-line options. Since this option couldn't be implemented in a
jq program, I'll probably do it. Though perhaps a generic utility to wrap
filters with might be better:

$ inplace some-file command args

which would run the given command with some-file as the standard input
and which would redirect the command's output to a temporary file in the
same directory then rename that into place. That's the Unix philosophy,
after all.

On Mon, Jun 9, 2014 at 10:58 AM, Nico Williams [email protected]
wrote:

I see. I've generally not bothered with sed's -i option. For me the
problem has been two-fold: a) it is (well, was) not portable enough, b) I
always have to check whether such a tool's in-place update functionality is
documented as working this way (rename into place), because I generally
need atomicity. And then there's Windows (where renames generally don't
work when there's open file handles for the file in question, unless all
the processes that opened it used an option to allow it).

On Mon, Jun 9, 2014 at 10:23 AM, ipmcc [email protected] wrote:

By analogy: Would it be acceptable if made you save to a different file
every time you wanted to save your document?

It's not that the problem can't be worked around, it's that doing so is
cumbersome. The fact that something as stream oriented as sed has an
in-place option is a pretty good indicator that this is common
expectation/desire.


Reply to this email directly or view it on GitHub
#105 (comment).

@nicowilliams
Copy link
Contributor

Ah, there's a sponge(1) that almost works this way:

http:https://joeyh.name/code/moreutils/
http:https://backreference.org/2011/01/29/in-place-editing-of-files/

I think both, sponge(1) and my idea of an inplace(1) would be very useful
generally and very much in keeping with the Unix philosophy.

Would that work for you?

@nicowilliams
Copy link
Contributor

@svnpenn @ipmcc @karelv

Regardless of how you feel about this issue, what think ye of this:

https://github.com/nicowilliams/inplace

?

@nicowilliams nicowilliams added this to the 1.5 release milestone Jun 11, 2014
@drorata
Copy link

drorata commented Oct 23, 2015

Is the -i option available or not? In the linked commit it says that this feature was removed.

@pkoppstein
Copy link
Contributor

@drorata - It was (as best I can tell) never available in any official jq release, and is certainly not available in jq 1.4 or 1.5. It was added as an experimental feature on Jul 20, 2014, and removed on Mar 5, 2015.

@pkoppstein
Copy link
Contributor

@svnpenn - Although there are obviously circumstances when -i would be convenient, jq is more like awk than sed. The earlier attempt to endow jq with -i confirms that such attempts will be fraught with difficulties. awks do not have an -i option, and I think it is asking too much of the jq developers/maintainers to devise and implement such an option, especially given there are easy workarounds.

@nicowilliams
Copy link
Contributor

nicowilliams commented Oct 23, 2015 via email

@pkoppstein
Copy link
Contributor

@svnpenn - My apologies if I over-generalized, but the GNU awk documentation characterizes "inplace editing" as "an extension" (https://www.gnu.org/software/gawk/manual/html_node/Extension-Sample-Inplace.html). Also, your example does not work with the GNU awk installed on my machine (4.0.2), and the awk that comes with OS X 10.9.5 does not support a "-i" option at all.

@dtolnay
Copy link
Member

dtolnay commented Oct 23, 2015

I agree that jq can and should do in-place. The earlier problems were due to a poor original implementation.

@nicowilliams
Copy link
Contributor

nicowilliams commented Oct 23, 2015 via email

@jqlang jqlang locked and limited conversation to collaborators Oct 24, 2015
@jqlang jqlang unlocked this conversation Nov 6, 2015
wtlangford pushed a commit to wtlangford/jq that referenced this issue Dec 1, 2015
This was an off-by one: the NUL terminator byte was not allocated on
resize.  This was triggered by JSON-encoded numbers longer than 256
bytes.
@danielbayley
Copy link

Is this feature in the next release? I found it in 'release candidate 1', or is that the version that was retracted?

@nicowilliams
Copy link
Contributor

@danielbayley No, it got taken out. 1.5rc1 implemented the wrong -i semantics., sorry.

@nicowilliams
Copy link
Contributor

nicowilliams commented Dec 7, 2015 via email

@nicowilliams nicowilliams reopened this Dec 7, 2015
@nicowilliams nicowilliams removed this from the 1.5 release milestone Dec 7, 2015
@ghost
Copy link
Author

ghost commented Apr 8, 2016

+1

@BrendanThompson
Copy link

any action on this one?

@devopscast
Copy link

devopscast commented Jun 11, 2021

because -i works nicely for multiple files

meh.. that's what tee(1) is for and that only works if your iterating over files which is again anti-practice;
for x in foo fu bar; do something $x; done Is a lot slower than xargs -I{} -P3 something {} <<< foo fu fubar or if one passes many '-i filename(s)' flags. In both cases they make for really unreadable scripting and a lot of codegolf'd one offs. Which is why I stand on the grounds of anti-practice.

My debate really isn't about should jq have an inplace flag, that might benefit many. But more about good scripting in general.

Most commands should be seen equally to a library method. jq just happens to give us the missing jsonpath method which other interpreter languages have baked in. In most programming best practices the fewer arguments you give to the method/function the better. Anything longer should be an interpreted file for repeatability (see awk -f for an example). Need that method/function to do more, write a wrapper to it.

@gentunian
Copy link

@omgdenzuko so GNU sed -i flag to you is a bad practice?

@panwarab
Copy link

panwarab commented Aug 9, 2021

I am honestly amazed. 😲

@samliddicott
Copy link

samliddicott commented Aug 10, 2021 via email

@MadsBuchmann
Copy link

I would like this feature 😄

@loynoir
Copy link

loynoir commented Sep 3, 2021

Although I use below, still want '-i' or '-w'

jq 'del(.packageManager)' package.json | sponge package.json

@ipleten
Copy link

ipleten commented Oct 20, 2021

It can be done with:

echo "$(jq '. + {"registry-mirrors": ["https://docker-mirror"]}' /etc/docker/daemon.json)" > /etc/docker/daemon.json

@devopscast
Copy link

devopscast commented Oct 20, 2021 via email

@loynoir
Copy link

loynoir commented Oct 20, 2021

Don't even need the echo.

On Wed, Oct 20, 2021, 18:02 Iaroslav Pleten @.***> wrote: It can be done with: echo "$(jq '. + {"registry-mirrors": ["https://docker-mirror"]}' /etc/docker/daemon.json)" > /etc/docker/daemon.json — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub <#105 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AUILSHGDJM7AUS5KQZ34FTTUH5DAJANCNFSM4AEYTROA .

@omgdenzuko Yes, you need, unless you are not using bash/zsh/...

@devopscast
Copy link

devopscast commented Oct 21, 2021 via email

@loynoir
Copy link

loynoir commented Oct 21, 2021

@omgdenzuko

Don't even need the echo.

$ echo '{"a":"b"}' > daemon.json && jq '. + {"registry-mirrors": ["https://docker-mirror"]}' daemon.json > daemon.json && cat daemon.json

$ echo '{"a":"b"}' > daemon.json && echo "$(jq '. + {"registry-mirrors": ["https://docker-mirror"]}' daemon.json)" > daemon.json && cat daemon.json

{
  "a": "b",
  "registry-mirrors": [
    "https://docker-mirror"
  ]
}

@loynoir
Copy link

loynoir commented Oct 21, 2021

@omgdenzuko
I'm not saying I'm disagreed.

jq '<expression>' > output.json works on all posix systems.

But not agree with your

Don't even need the echo.

Because jq '<expression>' inplace.json > inplace.json truncate inplace,
while @ipleten using echo "$(jq '<expression>' inplace.json)" > inplace.json to edit files in place

@devopscast
Copy link

devopscast commented Oct 21, 2021 via email

@loynoir
Copy link

loynoir commented Oct 21, 2021

@omgdenzuko
Oh, seems I misunderstood your commment

Don't even need the echo.

On ipleten's approach

echo "$(jq '<expression>' inplace.json)" > inplace.json

To

jq '<expression>' inplace.json > inplace.json

But seems you actaully means

jq '<expression>' inplace.json | tee inplace.json

I prefer sponge or echo approach then tee approach

https://stackoverflow.com/questions/33638511/differences-between-sponge-and-tee

@pkoppstein
Copy link
Contributor

Using > or tee is at best a bad idea unless your shell or version of tee specifically documents that it can be used in the desired way.

For more on this, see http:https://askubuntu.com/questions/752174

Using sponge is often the way to go.

@ipleten
Copy link

ipleten commented Oct 21, 2021

Hey!
I'm glad I triggered such discussion and really glad so many people know *nix shell. sponge isn't included by default in almost all docker containers(frankly I've never heard about it before).
I just put one of many possible solutions.
Thanks!

@devopscast
Copy link

devopscast commented Oct 21, 2021

@ipleten in any debian based distro sponge is apart of the moreutils and its not part of POSIX or heirloom *nix so that's why its not include by default.

A list of supported package managers to install sponge and it copy-pasta command can be found at https://command-not-found.com/sponge

@samliddicott
Copy link

samliddicott commented Oct 23, 2021 via email

@ryanotella
Copy link

yq does support inplace editing and can produce normal JSON. CLI API is different however.

$ echo '["first"]' > message.json
$ yq -i -ojson eval '. += ["second"]' message.json 
$ cat message.json
[
  "first",
  "second"
]

https://mikefarah.gitbook.io/yq/

@shakibamoshiri
Copy link

I use yq,
For advanced users this -i (in-place update) is needed, hope be added to jq

yq -iP '.Email.Port=3030' config.json -o json
  • -i in place update
  • -P pretty print
  • -o output should be json

@samliddicott
Copy link

After nearly 10 years of jq maintainers arguing against in-place editing on the grounds that there are difficult and obscure ways around that delightful limitation, we now also have jaq, written in rust, with in-place editing of multiple files, and fixing various jq bugs:

https://github.com/01mf02/jaq#readme

@drm
Copy link

drm commented Jun 16, 2023

I hope this PR can once and for all end this debate. It's a really simple change that follows exactly the solution that was explained in the Kernighan and Pink document as mentioned earlier, plus it doesn't impose any big changes on how the internals work.

Re yq and jaq: https://xkcd.com/927 comes to mind :P

@sdavids
Copy link

sdavids commented Jan 16, 2024

While we are waiting ...

Minify all *.json files in a given directory:

find . \
  -type f \
  -name '*.json' \
  -exec sh -c 'mv "$1" "$1.tmp"; jq -c . "$1.tmp" > "$1"; rm "$1.tmp"' shell {} \;

@luckman212
Copy link

@sdavids what does the shell right before the {} do in the above statement?

@sdavids
Copy link

sdavids commented Jan 16, 2024

The string sh. This will be available as $0 inside the script, and if the internal shell outputs an error message, it will prefix it with this string.

https://unix.stackexchange.com/questions/389705/understanding-the-exec-option-of-find#answer-389706

I used shell instead of sh so one does not mistakenly think that one can select a specific shell in that position—but the label is up to taste and whether you use $0 or not.

@piegamesde

This comment was marked as outdated.

@gentunian
Copy link

github community is getting weird...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests