Skip to content

Commit 1f4ba3c

Browse files
feat(cmd): Make "extract" arguments mroe closely mirror common archive tools (#653)
Usage: car extract --file file.car [--output output] [mydir/mysubdir] [mydir/mysubdir2/file] [mydir2] [myfile]... This also makes "extract" much more consistent with "create" and matching common archive tools: - Not specifying any paths results in a complete extraction. - To read from stdin a single dash ('-') is specified as the input file.
1 parent 7d2f4ac commit 1f4ba3c

4 files changed

Lines changed: 55 additions & 30 deletions

File tree

cmd/car/car.go

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,23 +100,21 @@ func main1() int {
100100
}},
101101
},
102102
{
103-
Name: "extract",
104-
Aliases: []string{"x"},
105-
Usage: "Extract the contents of a car when the car encodes UnixFS data",
106-
Action: ExtractCar,
107-
ArgsUsage: "[output directory|-]",
103+
Name: "extract",
104+
Aliases: []string{"x"},
105+
Usage: "Extract the contents of a car when the car encodes UnixFS data",
106+
Action: ExtractCar,
108107
Flags: []cli.Flag{
109108
&cli.StringFlag{
110109
Name: "file",
111-
Aliases: []string{"f"},
112-
Usage: "The car file to extract from, or stdin if omitted",
113-
Required: false,
110+
Aliases: []string{"f", "input", "i"},
111+
Usage: "The car file to extract from. Use a single dash ('-') to read from stdin",
114112
TakesFile: true,
115113
},
116114
&cli.StringFlag{
117-
Name: "path",
118-
Aliases: []string{"p"},
119-
Usage: "The unixfs path to extract",
115+
Name: "output",
116+
Aliases: []string{"o"},
117+
Usage: "The path to write into",
120118
Required: false,
121119
},
122120
&cli.BoolFlag{

cmd/car/extract.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,22 @@ var ErrNotDir = fmt.Errorf("not a directory")
2222

2323
// ExtractCar pulls files and directories out of a car
2424
func ExtractCar(c *cli.Context) error {
25+
if !c.IsSet("file") {
26+
return fmt.Errorf("a file source must be specified")
27+
}
28+
2529
outputDir, err := os.Getwd()
2630
if err != nil {
2731
return err
2832
}
29-
if c.Args().Present() {
30-
outputDir = c.Args().First()
33+
if c.IsSet("output") {
34+
outputDir = c.String("output")
3135
}
3236

3337
var store storage.ReadableStorage
3438
var roots []cid.Cid
3539

36-
if c.String("file") == "" {
40+
if c.String("file") == "-" {
3741
if f, ok := c.App.Reader.(*os.File); ok {
3842
stat, err := f.Stat()
3943
if err != nil {
@@ -71,19 +75,28 @@ func ExtractCar(c *cli.Context) error {
7175
ls.TrustedStorage = true
7276
ls.SetReadStorage(store)
7377

74-
path, err := pathSegments(c.String("path"))
75-
if err != nil {
76-
return err
78+
paths := c.Args().Slice()
79+
if len(paths) == 0 {
80+
paths = append(paths, "")
7781
}
7882

7983
var extractedFiles int
80-
for _, root := range roots {
81-
count, err := lib.ExtractToDir(c.Context, &ls, root, outputDir, path, c.IsSet("verbose"), c.App.ErrWriter)
84+
85+
for _, p := range paths {
86+
path, err := pathSegments(p)
8287
if err != nil {
8388
return err
8489
}
85-
extractedFiles += count
90+
91+
for _, root := range roots {
92+
count, err := lib.ExtractToDir(c.Context, &ls, root, outputDir, path, c.IsSet("verbose"), c.App.ErrWriter)
93+
if err != nil {
94+
return err
95+
}
96+
extractedFiles += count
97+
}
8698
}
99+
87100
if extractedFiles == 0 {
88101
return cli.Exit("no files extracted", 1)
89102
} else {

cmd/car/testdata/script/create-extract.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
car create --file=out.car foo.txt bar.txt
22
mkdir out
3-
car extract -v -f out.car out
3+
car extract -v -f out.car -o out
44
stderr -count=2 'txt$'
55
stderr -count=1 '^extracted 2 file\(s\)$'
66
car create --file=out2.car out/foo.txt out/bar.txt

cmd/car/testdata/script/extract.txt

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# full DAG export, everything in the CAR
22
mkdir actual-full
3-
car extract -f ${INPUTS}/simple-unixfs.car actual-full
3+
car extract -f ${INPUTS}/simple-unixfs.car -o actual-full
44
stderr '^extracted 9 file\(s\)$'
55
cmp actual-full/a/1/A.txt expected/a/1/A.txt
66
cmp actual-full/a/2/B.txt expected/a/2/B.txt
@@ -15,7 +15,7 @@ cmp actual-full/c/8/H.txt expected/c/8/H.txt
1515
# full DAG export, everything in the CAR, accepted from stdin
1616
mkdir actual-stdin
1717
stdin ${INPUTS}/simple-unixfs.car
18-
car extract actual-stdin
18+
car extract -f - -o actual-stdin
1919
stderr '^extracted 9 file\(s\)$'
2020
cmp actual-stdin/a/1/A.txt expected/a/1/A.txt
2121
cmp actual-stdin/a/2/B.txt expected/a/2/B.txt
@@ -29,7 +29,7 @@ cmp actual-stdin/c/8/H.txt expected/c/8/H.txt
2929

3030
# full DAG export, everything in the CAR, but the CAR is missing blocks (incomplete DAG)
3131
mkdir actual-missing
32-
car extract -f ${INPUTS}/simple-unixfs-missing-blocks.car actual-missing
32+
car extract -f ${INPUTS}/simple-unixfs-missing-blocks.car -o actual-missing
3333
stderr -count=1 'data for entry not found: /b/4 \(skipping\.\.\.\)'
3434
stderr -count=1 'data for entry not found: /b/5/E.txt \(skipping\.\.\.\)'
3535
stderr -count=1 'data for entry not found: /b/6 \(skipping\.\.\.\)'
@@ -46,7 +46,7 @@ cmp actual-missing/c/8/H.txt expected/c/8/H.txt
4646

4747
# path-based partial export, everything under the path specified (also without leading / in path)
4848
mkdir actual-partial
49-
car extract -f ${INPUTS}/simple-unixfs.car -p b actual-partial
49+
car extract -f ${INPUTS}/simple-unixfs.car -o actual-partial b
5050
stderr '^extracted 3 file\(s\)$'
5151
! exists actual-partial/a/1/A.txt
5252
! exists actual-partial/a/2/B.txt
@@ -60,7 +60,7 @@ cmp actual-partial/b/4/D.txt expected/b/4/D.txt
6060

6161
# path-based single-file export (also with leading /)
6262
mkdir actual-single
63-
car extract -f ${INPUTS}/simple-unixfs.car -p /a/2/B.txt actual-single
63+
car extract -f ${INPUTS}/simple-unixfs.car -o actual-single /a/2/B.txt
6464
stderr '^extracted 1 file\(s\)$'
6565
! exists actual-single/a/1/A.txt
6666
cmp actual-single/a/2/B.txt expected/a/2/B.txt
@@ -72,18 +72,32 @@ cmp actual-single/a/2/B.txt expected/a/2/B.txt
7272
! exists actual-single/c/7/G.txt
7373
! exists actual-single/c/8/H.txt
7474

75+
# path-based multiple export
76+
mkdir actual-multiple
77+
car extract -f ${INPUTS}/simple-unixfs.car -o actual-multiple /a b/6 /c/7/G.txt
78+
stderr '^extracted 5 file\(s\)$'
79+
cmp actual-multiple/a/1/A.txt expected/a/1/A.txt
80+
cmp actual-multiple/a/2/B.txt expected/a/2/B.txt
81+
cmp actual-multiple/a/3/C.txt expected/a/3/C.txt
82+
! exists actual-multiple/b/5/E.txt
83+
cmp actual-multiple/b/6/F.txt expected/b/6/F.txt
84+
! exists actual-multiple/b/4/D.txt
85+
! exists actual-multiple/c/9/I.txt
86+
cmp actual-multiple/c/7/G.txt expected/c/7/G.txt
87+
! exists actual-multiple/c/8/H.txt
88+
7589
# extract that doesn't yield any files should error
76-
! car extract -f ${INPUTS}/simple-unixfs-missing-blocks.car -p b
90+
! car extract -f ${INPUTS}/simple-unixfs-missing-blocks.car b
7791
stderr '^no files extracted$'
7892

7993
# car with only one file, nested inside sharded directory, output to stdout
80-
car extract -f ${INPUTS}/wikipedia-cryptographic-hash-function.car -p wiki/Cryptographic_hash_function -
94+
car extract -f ${INPUTS}/wikipedia-cryptographic-hash-function.car -o - wiki/Cryptographic_hash_function
8195
stderr '^extracted 1 file\(s\)$'
8296
stdout -count=1 '^ <title>Cryptographic hash function</title>$'
8397

8498
# car with only one file, full extract, lots of errors
8599
mkdir actual-wiki
86-
car extract -f ${INPUTS}/wikipedia-cryptographic-hash-function.car actual-wiki
100+
car extract -f ${INPUTS}/wikipedia-cryptographic-hash-function.car -o actual-wiki
87101
stderr '^extracted 1 file\(s\)$'
88102
stderr -count=1 '^data for entry not found for 570 unknown sharded entries \(skipped\.\.\.\)$'
89103
# random sampling of expected skip errors
@@ -110,4 +124,4 @@ c9I
110124
-- expected/c/7/G.txt --
111125
c7G
112126
-- expected/c/8/H.txt --
113-
c8H
127+
c8H

0 commit comments

Comments
 (0)