Julia: Julia `cp` isn't consistent with command-line `cp` with destination "."

Created on 15 Jul 2020  ·  4Comments  ·  Source: JuliaLang/julia

I noticed this inconsistency between the Julia cp and the command-line cp:

julia> touch("file");

julia> mkdir("dir");

julia> cp("file", joinpath("dir", "."))
ERROR: ArgumentError: 'dir/.' exists. `force=true` is required to remove 'dir/.' before copying.
Stacktrace:
 [1] checkfor_mv_cp_cptree(src::String, dst::String, txt::String; force::Bool)
   @ Base.Filesystem ./file.jl:308
 [2] cp(src::String, dst::String; force::Bool, follow_symlinks::Bool)
   @ Base.Filesystem ./file.jl:345
 [3] cp(src::String, dst::String)
   @ Base.Filesystem ./file.jl:345
 [4] top-level scope
   @ REPL[3]:1

To edit a specific method, type the corresponding number into the REPL and press Ctrl+Q

shell> cp file dir/.

julia> isfile(joinpath("dir", "file"))
true

Additionally, when using . with force=true Julia attempts to delete the directory instead of the file:

julia> cp("file", joinpath("dir", "."), force=true)
ERROR: SystemError (with dir/.): rmdir: Invalid argument
Stacktrace:
 [1] systemerror(p::Symbol, errno::Int32; extrainfo::String)
   @ Base ./error.jl:168
 [2] #systemerror#51
   @ ./error.jl:167 [inlined]
 [3] rm(path::String; force::Bool, recursive::Bool)
   @ Base.Filesystem ./file.jl:286
 [4] checkfor_mv_cp_cptree(src::String, dst::String, txt::String; force::Bool)
   @ Base.Filesystem ./file.jl:306
 [5] cp(src::String, dst::String; force::Bool, follow_symlinks::Bool)
   @ Base.Filesystem ./file.jl:345
 [6] top-level scope
   @ REPL[8]:1

To edit a specific method, type the corresponding number into the REPL and press Ctrl+Q

shell> cp -f file dir/.
filesystem

All 4 comments

I would argue that the fact that the cp command does different things depending on whether the destination is a directory or not is undesirable in a programmatic API. It means that when you write code that does cp(src, dst) you don't know if the end result is that dst is a copy of src or joinpath(dst, basename(src)) is a copy of src. That behavior is fine in a purely interactive tool but bad for programmatic use.

That's a reasonable position. I would say we should probably add a note to the docstring mentioning this difference

Similar but far more destructive:

julia> touch("file")
"file"

julia> mkdir("dir")
"dir"

julia> touch(joinpath("dir", "important-file"))
"dir/important-file"

julia> collect(walkdir("."))
2-element Vector{Any}:
 (".", ["dir"], ["file"])
 ("./dir", String[], ["important-file"])

julia> cp("file", "dir/", force=true)
ERROR: IOError: open: no such file or directory (ENOENT)
Stacktrace:
 [1] uv_error
   @ ./libuv.jl:97 [inlined]
 [2] open(path::String, flags::UInt16, mode::UInt64)
   @ Base.Filesystem ./filesystem.jl:87
 [3] sendfile(src::String, dst::String)
   @ Base.Filesystem ./file.jl:911
 [4] cp(src::String, dst::String; force::Bool, follow_symlinks::Bool)
   @ Base.Filesystem ./file.jl:351
 [5] top-level scope
   @ REPL[5]:1

To edit a specific method, type the corresponding number into the REPL and press Ctrl+Q

julia> cp("file", "dir", force=true)
"dir"

julia> collect(walkdir("."))
1-element Vector{Any}:
 (".", String[], ["dir", "file"])

I dunno, seems like that's what force=true would do 🤷‍♂️

Was this page helpful?
0 / 5 - 0 ratings

Related issues

arshpreetsingh picture arshpreetsingh  ·  3Comments

dpsanders picture dpsanders  ·  3Comments

yurivish picture yurivish  ·  3Comments

Keno picture Keno  ·  3Comments

TotalVerb picture TotalVerb  ·  3Comments