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

Option for recursive rmdir #7055

Merged
merged 6 commits into from
Jun 20, 2014
Merged

Conversation

hayd
Copy link
Member

@hayd hayd commented May 31, 2014

At the moment to rm -rf you need to shell out, with this:

julia> mkpath("foo/bar/baz.jl")

julia> rmdir("foo")
ERROR: rmdir: Directory not empty
 in rmdir at file.jl:63
 in rmdir at file.jl:55

julia> rmdir("foo", true) # rm -rf

see also #7046.

if recursive
for p=readdir(path)
p = joinpath(path, p)
isfile(p) && rm(p) || rmdir(p, true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see how this will work, because rm(p) doesn't return a boolean, but its value is needed in order for Julia whether to evaluate the rmdir in the ||. (On my machine with Julia 0.3, it gives ERROR: type: non-boolean (Nothing) used in boolean context.)

Better to just use a plain if ... else ... end expression here or a isfile(p) ? rm(p) : rmdir(p,true) if you want a one-liner.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also doesn't work for symbolic links, since isfile(p) currently returns false for a symbolic link to a directory, and yet rmdir(p) doesn't work for symbolic links to directories.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! you're right of course! I will break into a for loop.

If I use isdir here instead would this work with sym links (are they are counted as files - so we just want to rm them) ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you want to just rm links. But isdir won't work: it returns true for a symlink to a directory.

I think you need isfile(p) || islink(p) ? rm(p) : rmdir(p, true)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, to avoid a second call to stat, it might be useful to add something like

isfileorlink(st::StatStruct) = st.mode & 0xd000 == 0x8000
isfileorlink(path...) = isfileorlink(lstat(path...))

to stat.jl

@stevengj
Copy link
Member

Also, you should add a test case to test/file.jl.

@hayd
Copy link
Member Author

hayd commented Jun 4, 2014

@stevengj I've added isfileorlink to stat like you suggest and a test of rmdir(.., true).

This now works for me with links/files/dirs, but ATM there aren't any tests which use linked files. I could add that some tests for islink (but don't know how to do this without shelling out: ln -s) hayd@474c025 (which passes travis)?

@@ -150,7 +150,7 @@ function clone(url::String, pkg::String)
Git.run(`clone -q $url $pkg`)
Git.set_remote_url(url, dir=pkg)
catch
run(`rm -rf $pkg`)
rmdir(pkg)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing true here

@stevengj
Copy link
Member

stevengj commented Jun 4, 2014

Note that you also need to update the docs for rmdir.

@JeffBezanson
Copy link
Sponsor Member

Bump. Looks pretty good.
cc @StefanKarpinski

@StefanKarpinski
Copy link
Sponsor Member

Two API issues:

  1. I'm not convinced that we need isfileorlink. What does that exclude from ispath? That the path is a directory? If the path is a link it could be a link to a file or a directory.
  2. I wonder if we shouldn't just make rm delete anything, always recursively. The fact that the shell rm command doesn't do this, is largely because it's intended to be used interactively.

@JeffBezanson
Copy link
Sponsor Member

Yeah, maybe the right test is islink(p) || !isdir(p). fifos and so on should be removed by rm too.

@nalimilan
Copy link
Member

About 2., I'd rather add a recursive option to rm, defaulting to false. It may offer additional safety when you want to remove a single file, either from a function or interactively. If for some reason the path gets wrong, you'd avoid destroying a whole directory tree.

@StefanKarpinski
Copy link
Sponsor Member

There's a combinatorial explosion of things you can test a file stat for. We provide convenience accessors for the basic ones, but this is starting down the path of providing all 2^n of them. When performance is an issue, you can use the stat function explicitly and then combines the tests.

@StefanKarpinski
Copy link
Sponsor Member

@nalimilan, you may be right that it's best not to recursively delete directories without some insistence.

@hayd
Copy link
Member Author

hayd commented Jun 9, 2014

Happy to remove the isfileorlink if we don't think perf is an issue here.

IMO the API is quite nice atm that rm removes files or links, and rmdir removes directories (in this branch adding the recursive flag if you specifically want to remove it's contents), to me this seems a useful distinction. Allowing rm with recursive on non-empty directories but rmdir on empty ones seems a strange choice (or are you suggesting removing rmdir entirely?).

@StefanKarpinski
Copy link
Sponsor Member

I was suggesting removing rmdir entirely. I agree that keeping rmdir and adding recursive rm is a weird intermediate situation.

@nalimilan
Copy link
Member

I agree that keeping rmdir and adding recursive rm is a weird intermediate situation.

And yet that's the situation of all Linux (POSIX, even?) systems I know of. :-)

I'm OK with keeping only rm, which would remove only single files or empty directories by default, with a recursive argument to remove more than that.

@StefanKarpinski
Copy link
Sponsor Member

I keep not being able to find this because it doesn't link to #7041 anywhere. Now it does.

@staticfloat
Copy link
Sponsor Member

I too suggest an rm with a recursive option to enable deletion of non-empty directories. The splitting out of rmdir has always seemed a very arbitrary thing to me in every programming language I've encountered it in.

@StefanKarpinski
Copy link
Sponsor Member

How about having rm to delete any single thing – a file, link, empty directory – and rm(path; recurse=true) to delete something recursively, which deletes non-empty directories recursively?

@staticfloat
Copy link
Sponsor Member

Works for me.

@tkelman
Copy link
Contributor

tkelman commented Jun 16, 2014

There is now symlink functionality in base (#4396), so the tests here should probably be updated to account for that. Will want to test that this doesn't recurse into the targets of symlinks or NTFS junctions on Windows, either.

@vtjnash vtjnash added this to the 0.3 milestone Jun 16, 2014
@JeffBezanson JeffBezanson merged commit 2b3cf04 into JuliaLang:master Jun 20, 2014
@StefanKarpinski
Copy link
Sponsor Member

Yeah? You decided isfileorlink belongs in Base?

@tkelman
Copy link
Contributor

tkelman commented Jun 20, 2014

He merged then fixed it a2d4dc9

@StefanKarpinski
Copy link
Sponsor Member

Ah, cool.

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

Successfully merging this pull request may close these issues.

None yet

8 participants