Repository: peak/s5cmd (go)

Issue:
Print destination for copy/move operations
Example:

`copy: dir/file s3://bucket/prefix/object`

Also maybe we should print `time` and `log level` as well, such as;

`2020-03-24 10:10:10 INFO "copy: dir/file s3://bucket/prefix/object"`

Pull request description:
Print destination url on info logs
Resolves #120 

Patch:
diff --git a/README.md b/README.md
index c9144f0..5a71370 100644
--- a/README.md
+++ b/README.md
@@ -234,9 +234,9 @@ retries with `--retry-count` option.
 * text format
 
 ```shell
-$ s5cmd cp s3://bucket/key .
+$ s5cmd cp s3://bucket/testfile .
 
-download s3://bucket/key
+cp s3://bucket/testfile testfile
 ```
 
 ```shell
@@ -249,14 +249,14 @@ ERROR "cp s3://somebucket/file.txt file.txt": object already exists
 
 ```json
     {
-      "operation": "download",
+      "operation": "cp",
       "success": true,
-      "source": "s3://bucket/key",
-      "destination": ".",
+      "source": "s3://bucket/testfile",
+      "destination": "testfile",
       "object": "[object]"
     }
     {
-      "operation": "download",
+      "operation": "cp",
       "job": "cp s3://somebucket/file.txt file.txt",
       "error": "'cp s3://somebucket/file.txt file.txt': object already exists"
     }
diff --git a/command/cmd_delete.go b/command/cmd_delete.go
index 7c6c62c..e9f3155 100644
--- a/command/cmd_delete.go
+++ b/command/cmd_delete.go
@@ -47,11 +47,7 @@ var DeleteCommand = &cli.Command{
 			return fmt.Errorf("expected at least 1 object to remove")
 		}
 
-		if err := checkSources(c.Args().Slice()...); err != nil {
-			return err
-		}
-
-		return nil
+		return checkSources(c.Args().Slice()...)
 	},
 	Action: func(c *cli.Context) error {
 		return Delete(
diff --git a/log/message.go b/log/message.go
index 07a2f5f..a573c21 100644
--- a/log/message.go
+++ b/log/message.go
@@ -25,6 +25,9 @@ type InfoMessage struct {
 
 // String is the string representation of InfoMessage.
 func (i InfoMessage) String() string {
+	if i.Destination != nil {
+		return fmt.Sprintf("%v %v %v", i.Operation, i.Source, i.Destination)
+	}
 	return fmt.Sprintf("%v %v", i.Operation, i.Source)
 }
 
diff --git a/storage/url/url.go b/storage/url/url.go
index 925e592..412ca5d 100644
--- a/storage/url/url.go
+++ b/storage/url/url.go
@@ -274,10 +274,7 @@ func (u *URL) Match(key string) bool {
 }
 
 func (u *URL) String() string {
-	if u.IsRemote() {
-		return u.Absolute()
-	}
-	return u.Base()
+	return u.Absolute()
 }
 
 func (u *URL) MarshalJSON() ([]byte, error) {


Test patch:
diff --git a/e2e/cp_test.go b/e2e/cp_test.go
index 069b9d2..fabfb49 100644
--- a/e2e/cp_test.go
+++ b/e2e/cp_test.go
@@ -91,7 +91,7 @@ func TestCopySingleS3ObjectToLocal(t *testing.T) {
 			result.Assert(t, icmd.Success)
 
 			assertLines(t, result.Stdout(), map[int]compareFunc{
-				0: equals(`cp s3://%v/%v`, bucket, tc.src),
+				0: equals(`cp s3://%v/%v %v`, bucket, tc.src, tc.dst),
 				1: equals(""),
 			})
 
@@ -258,10 +258,10 @@ func TestCopyMultipleFlatS3ObjectsToLocal(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp s3://%v/another_test_file.txt`, bucket),
-		2: equals(`cp s3://%v/filename-with-hypen.gz`, bucket),
-		3: equals(`cp s3://%v/readme.md`, bucket),
-		4: equals(`cp s3://%v/testfile1.txt`, bucket),
+		1: equals(`cp s3://%v/another_test_file.txt another_test_file.txt`, bucket),
+		2: equals(`cp s3://%v/filename-with-hypen.gz filename-with-hypen.gz`, bucket),
+		3: equals(`cp s3://%v/readme.md readme.md`, bucket),
+		4: equals(`cp s3://%v/testfile1.txt testfile1.txt`, bucket),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -308,8 +308,8 @@ func TestCopyMultipleFlatS3ObjectsToLocalWithPartialMatching(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp s3://%v/another_test_file.txt`, bucket),
-		2: equals(`cp s3://%v/testfile1.txt`, bucket),
+		1: equals(`cp s3://%v/another_test_file.txt another_test_file.txt`, bucket),
+		2: equals(`cp s3://%v/testfile1.txt testfile1.txt`, bucket),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -354,7 +354,7 @@ func TestCopyMultipleNestedS3ObjectsToLocalWithPartialMatching(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp s3://%v/a/b/testfile2.txt`, bucket),
+		1: equals(`cp s3://%v/a/b/testfile2.txt testfile2.txt`, bucket),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -494,11 +494,11 @@ func TestCopyMultipleNestedS3ObjectsToLocal(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp s3://%v/a/b/filename-with-hypen.gz`, bucket),
-		2: equals(`cp s3://%v/a/readme.md`, bucket),
-		3: equals(`cp s3://%v/b/another_test_file.txt`, bucket),
-		4: equals(`cp s3://%v/c/d/e/another_test_file.txt`, bucket),
-		5: equals(`cp s3://%v/testfile1.txt`, bucket),
+		1: equals(`cp s3://%v/a/b/filename-with-hypen.gz filename-with-hypen.gz`, bucket),
+		2: equals(`cp s3://%v/a/readme.md readme.md`, bucket),
+		3: equals(`cp s3://%v/b/another_test_file.txt another_test_file.txt`, bucket),
+		4: equals(`cp s3://%v/c/d/e/another_test_file.txt another_test_file.txt`, bucket),
+		5: equals(`cp s3://%v/testfile1.txt testfile1.txt`, bucket),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -548,8 +548,8 @@ func TestCopyMultipleNestedS3ObjectsToLocalWithParents(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp s3://%v/b/another_test_file.txt`, bucket),
-		2: equals(`cp s3://%v/c/d/e/another_test_file.txt`, bucket),
+		1: equals(`cp s3://%v/b/another_test_file.txt b/another_test_file.txt`, bucket),
+		2: equals(`cp s3://%v/c/d/e/another_test_file.txt c/d/e/another_test_file.txt`, bucket),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -609,10 +609,10 @@ func TestCopyMultipleS3ObjectsToGivenLocalDirectory(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp s3://%v/another_test_file.txt`, bucket),
-		2: equals(`cp s3://%v/filename-with-hypen.gz`, bucket),
-		3: equals(`cp s3://%v/readme.md`, bucket),
-		4: equals(`cp s3://%v/testfile1.txt`, bucket),
+		1: equals(`cp s3://%v/another_test_file.txt %v/another_test_file.txt`, bucket, dst),
+		2: equals(`cp s3://%v/filename-with-hypen.gz %v/filename-with-hypen.gz`, bucket, dst),
+		3: equals(`cp s3://%v/readme.md %v/readme.md`, bucket, dst),
+		4: equals(`cp s3://%v/testfile1.txt %v/testfile1.txt`, bucket, dst),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -673,7 +673,7 @@ func TestCopySingleFileToS3(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: suffix(`cp %v`, filename),
+		0: suffix(`cp %v %v%v`, srcpath, dstpath, filename),
 		1: equals(""),
 	})
 
@@ -713,7 +713,7 @@ func TestCopySingleFileToS3JSON(t *testing.T) {
 		{
 			"operation": "cp",
 			"success": true,
-			"source": "testfile1.txt",
+			"source": "%v",
 			"destination": "s3://%v/testfile1.txt",
 			"object": {
 				"type": "file",
@@ -725,7 +725,7 @@ func TestCopySingleFileToS3JSON(t *testing.T) {
 
 	result.Assert(t, icmd.Success)
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: json(jsonText, bucket),
+		0: json(jsonText, fpath, bucket),
 		1: equals(""),
 	}, jsonCheck(true))
 
@@ -759,19 +759,21 @@ func TestCopyDirToS3(t *testing.T) {
 
 	workdir := fs.NewDir(t, t.Name(), folderLayout...)
 	defer workdir.Remove()
+	srcpath := workdir.Path()
+	dstpath := fmt.Sprintf("s3://%v/", bucket)
 
 	// this command ('s5cmd cp dir/ s3://bucket/') will run in 'walk' mode,
 	// which is different than 'glob' mode.
-	cmd := s5cmd("cp", workdir.Path()+"/", "s3://"+bucket+"/")
+	cmd := s5cmd("cp", workdir.Path()+"/", dstpath)
 	result := icmd.RunCmd(cmd)
 
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: suffix(`cp file1.txt`),
-		2: contains(`cp file2.txt`),
-		3: contains(`cp readme.md`),
+		1: equals(`cp %s/c/file2.txt %sfile2.txt`, srcpath, dstpath),
+		2: equals(`cp %s/file1.txt %sfile1.txt`, srcpath, dstpath),
+		3: equals(`cp %s/readme.md %sreadme.md`, srcpath, dstpath),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -810,19 +812,20 @@ func TestCopyMultipleFilesToS3Bucket(t *testing.T) {
 	}
 
 	workdir := fs.NewDir(t, "somedir", files...)
+	dstpath := fmt.Sprintf("s3://%v/", bucket)
+	srcpath := workdir.Path()
 	defer workdir.Remove()
 
-	cmd := s5cmd("cp", workdir.Path()+"/*", "s3://"+bucket+"/")
+	cmd := s5cmd("cp", srcpath+"/*", dstpath)
 	result := icmd.RunCmd(cmd)
 
 	result.Assert(t, icmd.Success)
-
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp another_test_file.txt`),
-		2: equals(`cp filename-with-hypen.gz`),
-		3: equals(`cp readme.md`),
-		4: equals(`cp testfile1.txt`),
+		1: equals(`cp %s/another_test_file.txt %sanother_test_file.txt`, srcpath, dstpath),
+		2: equals(`cp %s/filename-with-hypen.gz %vfilename-with-hypen.gz`, srcpath, dstpath),
+		3: equals(`cp %s/readme.md %vreadme.md`, srcpath, dstpath),
+		4: equals(`cp %s/testfile1.txt %vtestfile1.txt`, srcpath, dstpath),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -907,7 +910,9 @@ func TestCopyMultipleFilesToS3WithPrefixWithSlash(t *testing.T) {
 	workdir := fs.NewDir(t, "somedir", files...)
 	defer workdir.Remove()
 
-	src := fmt.Sprintf("%v/*", workdir.Path())
+	srcpath := workdir.Path()
+
+	src := fmt.Sprintf("%v/*", srcpath)
 	dst := fmt.Sprintf("s3://%v/prefix/", bucket)
 
 	cmd := s5cmd("cp", src, dst)
@@ -917,10 +922,10 @@ func TestCopyMultipleFilesToS3WithPrefixWithSlash(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp another_test_file.txt`),
-		2: equals(`cp filename-with-hypen.gz`),
-		3: equals(`cp readme.md`),
-		4: equals(`cp testfile1.txt`),
+		1: equals(`cp %v/another_test_file.txt %vanother_test_file.txt`, srcpath, dst),
+		2: equals(`cp %v/filename-with-hypen.gz %vfilename-with-hypen.gz`, srcpath, dst),
+		3: equals(`cp %v/readme.md %vreadme.md`, srcpath, dst),
+		4: equals(`cp %v/testfile1.txt %vtestfile1.txt`, srcpath, dst),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -971,10 +976,10 @@ func TestCopyLocalDirectoryToS3WithPrefixWithSlash(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp another_test_file.txt`),
-		2: equals(`cp filename-with-hypen.gz`),
-		3: equals(`cp readme.md`),
-		4: equals(`cp testfile1.txt`),
+		1: equals(`cp %vanother_test_file.txt %vanother_test_file.txt`, src, dst),
+		2: equals(`cp %vfilename-with-hypen.gz %vfilename-with-hypen.gz`, src, dst),
+		3: equals(`cp %vreadme.md %vreadme.md`, src, dst),
+		4: equals(`cp %vtestfile1.txt %vtestfile1.txt`, src, dst),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -1061,7 +1066,7 @@ func TestCopySingleS3ObjectToS3(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: suffix(`cp %v`, src),
+		0: suffix(`cp %v %v`, src, dst),
 		1: equals(""),
 	})
 
@@ -1154,7 +1159,7 @@ func TestCopySingleS3ObjectIntoAnotherBucket(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp %v`, src),
+		0: equals(`cp %v %v%v`, src, dst, filename),
 		1: equals(""),
 	})
 
@@ -1196,7 +1201,7 @@ func TestCopySingleS3ObjectIntoAnotherBucketWithObjName(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp %v`, src),
+		0: equals(`cp %v %v`, src, dst),
 		1: equals(""),
 	})
 
@@ -1234,7 +1239,7 @@ func TestCopySingleS3ObjectIntoAnotherBucketWithPrefix(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp %v`, src),
+		0: equals(`cp %v %v`, src, dst),
 		1: equals(""),
 	})
 
@@ -1277,10 +1282,10 @@ func TestCopyMultipleS3ObjectsToS3WithPrefix(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp s3://%v/another_test_file.txt`, bucket),
-		2: equals(`cp s3://%v/filename-with-hypen.gz`, bucket),
-		3: equals(`cp s3://%v/readme.md`, bucket),
-		4: equals(`cp s3://%v/testfile1.txt`, bucket),
+		1: equals(`cp s3://%v/another_test_file.txt %vanother_test_file.txt`, bucket, dst),
+		2: equals(`cp s3://%v/filename-with-hypen.gz %vfilename-with-hypen.gz`, bucket, dst),
+		3: equals(`cp s3://%v/readme.md %vreadme.md`, bucket, dst),
+		4: equals(`cp s3://%v/testfile1.txt %vtestfile1.txt`, bucket, dst),
 	}, sortInput(true))
 
 	// assert s3 source objects
@@ -1435,8 +1440,8 @@ func TestCopyMultipleS3ObjectsToS3_Issue70(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp s3://%v/config/.local/folder1/file1.txt`, bucket),
-		2: equals(`cp s3://%v/config/.local/folder2/file2.txt`, bucket),
+		1: equals(`cp s3://%v/config/.local/folder1/file1.txt %vfolder1/file1.txt`, bucket, dst),
+		2: equals(`cp s3://%v/config/.local/folder2/file2.txt %vfolder2/file2.txt`, bucket, dst),
 	}, sortInput(true))
 
 	// assert s3 source objects
@@ -1477,7 +1482,7 @@ func TestCopyS3ObjectToLocalWithTheSameFilename(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals("cp s3://%v/%v", bucket, filename),
+		0: equals("cp s3://%v/%v %v", bucket, filename, filename),
 		1: equals(""),
 	})
 
@@ -1554,7 +1559,7 @@ func TestCopyS3ToLocalWithSameFilenameOverrideIfSizeDiffers(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp s3://%v/%v`, bucket, filename),
+		0: equals(`cp s3://%v/%v %v`, bucket, filename, filename),
 		1: equals(""),
 	})
 
@@ -1598,7 +1603,7 @@ func TestCopyS3ToLocalWithSameFilenameOverrideIfSourceIsNewer(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp s3://%v/%v`, bucket, filename),
+		0: equals(`cp s3://%v/%v %v`, bucket, filename, filename),
 		1: equals(""),
 	})
 
@@ -1688,8 +1693,8 @@ func TestCopyS3ToLocal_Issue70(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp s3://%v/config/.local/folder1/file1.txt`, bucket),
-		2: equals(`cp s3://%v/config/.local/folder2/file2.txt`, bucket),
+		1: equals(`cp s3://%v/config/.local/folder1/file1.txt %v/folder1/file1.txt`, bucket, dstpath),
+		2: equals(`cp s3://%v/config/.local/folder2/file2.txt %v/folder2/file2.txt`, bucket, dstpath),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -1733,13 +1738,14 @@ func TestCopyLocalFileToS3WithTheSameFilename(t *testing.T) {
 	workdir := fs.NewDir(t, t.Name(), fs.WithFile(filename, newContent))
 	defer workdir.Remove()
 
-	cmd := s5cmd("cp", filename, "s3://"+bucket)
+	dst := "s3://" + bucket
+	cmd := s5cmd("cp", filename, dst)
 	result := icmd.RunCmd(cmd, withWorkingDir(workdir))
 
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp %v`, filename),
+		0: equals(`cp %v %v/%v`, filename, dst, filename),
 		1: equals(""),
 	})
 
@@ -1816,13 +1822,14 @@ func TestCopyLocalFileToS3WithNoClobber(t *testing.T) {
 	workdir := fs.NewDir(t, t.Name(), fs.WithFile(filename, newContent))
 	defer workdir.Remove()
 
-	cmd := s5cmd("cp", "-n", filename, "s3://"+bucket)
+	dst := "s3://" + bucket
+	cmd := s5cmd("cp", "-n", filename, dst)
 	result := icmd.RunCmd(cmd, withWorkingDir(workdir))
 
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp %v`, filename),
+		0: equals(`cp %v %v/%v`, filename, dst, filename),
 		1: equals(""),
 	})
 
@@ -1860,7 +1867,8 @@ func TestCopyLocalFileToS3WithSameFilenameOverrideIfSizeDiffers(t *testing.T) {
 	// upload a modified version of the file
 	putFile(t, s3client, bucket, filename, content)
 
-	cmd := s5cmd("cp", "-n", "-s", filename, "s3://"+bucket)
+	dst := "s3://" + bucket
+	cmd := s5cmd("cp", "-n", "-s", filename, dst)
 	result := icmd.RunCmd(cmd, withWorkingDir(workdir))
 
 	// '-n' prevents overriding the file, but '-s' overrides '-n' if the file
@@ -1868,7 +1876,7 @@ func TestCopyLocalFileToS3WithSameFilenameOverrideIfSizeDiffers(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp %v`, filename),
+		0: equals(`cp %v %v/%v`, filename, dst, filename),
 		1: equals(""),
 	})
 
@@ -1907,7 +1915,8 @@ func TestCopyLocalFileToS3WithSameFilenameOverrideIfSourceIsNewer(t *testing.T)
 	workdir := fs.NewDir(t, t.Name(), fs.WithFile(filename, expectedContent, timestamp))
 	defer workdir.Remove()
 
-	cmd := s5cmd("cp", "-n", "-u", filename, "s3://"+bucket)
+	dst := "s3://" + bucket
+	cmd := s5cmd("cp", "-n", "-u", filename, dst)
 	result := icmd.RunCmd(cmd, withWorkingDir(workdir))
 
 	// '-n' prevents overriding the file, but '-u' overrides '-n' if the file
@@ -1915,7 +1924,7 @@ func TestCopyLocalFileToS3WithSameFilenameOverrideIfSourceIsNewer(t *testing.T)
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp %v`, filename),
+		0: equals(`cp %v %v/%v`, filename, dst, filename),
 		1: equals(""),
 	})
 
@@ -2000,7 +2009,7 @@ func TestCopyLocalFileToS3WithCustomName(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp %v`, filename),
+		0: equals(`cp %v %v`, filename, dstpath),
 		1: equals(""),
 	})
 
@@ -2039,7 +2048,7 @@ func TestCopyLocalFileToS3WithPrefix(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp %v`, filename),
+		0: equals(`cp %v %v%v`, filename, dstpath, filename),
 		1: equals(""),
 	})
 
@@ -2078,7 +2087,7 @@ func TestMultipleLocalFileToS3Bucket(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`cp %v`, filename),
+		0: equals(`cp %v %v/%v`, filename, dstpath, filename),
 		1: equals(""),
 	})
 
@@ -2146,9 +2155,9 @@ func TestCopyMultipleLocalNestedFilesToS3WithParents(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals("cp file1.txt"),
-		2: equals("cp file2.txt"),
-		3: equals("cp readme.md"),
+		1: equals("cp a/file1.txt %va/file1.txt", dst),
+		2: equals("cp a/readme.md %va/readme.md", dst),
+		3: equals("cp b/c/file2.txt %vb/c/file2.txt", dst),
 	}, sortInput(true))
 
 	// assert local filesystem
diff --git a/e2e/mv_test.go b/e2e/mv_test.go
index b640456..1ccb552 100644
--- a/e2e/mv_test.go
+++ b/e2e/mv_test.go
@@ -33,7 +33,7 @@ func TestMoveSingleS3ObjectToLocal(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`mv s3://%v/%v`, bucket, filename),
+		0: equals(`mv s3://%v/%v %v`, bucket, filename, filename),
 		1: equals(""),
 	})
 
@@ -74,10 +74,10 @@ func TestMoveMultipleFlatS3ObjectsToLocal(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`mv s3://%v/another_test_file.txt`, bucket),
-		2: equals(`mv s3://%v/filename-with-hypen.gz`, bucket),
-		3: equals(`mv s3://%v/readme.md`, bucket),
-		4: equals(`mv s3://%v/testfile1.txt`, bucket),
+		1: equals(`mv s3://%v/another_test_file.txt another_test_file.txt`, bucket),
+		2: equals(`mv s3://%v/filename-with-hypen.gz filename-with-hypen.gz`, bucket),
+		3: equals(`mv s3://%v/readme.md readme.md`, bucket),
+		4: equals(`mv s3://%v/testfile1.txt testfile1.txt`, bucket),
 	}, sortInput(true))
 
 	// assert local filesystem
@@ -114,13 +114,14 @@ func TestMoveSingleFileToS3(t *testing.T) {
 	fpath := file.Path()
 	filename := filepath.Base(file.Path())
 
-	cmd := s5cmd("mv", fpath, "s3://"+bucket+"/")
+	dst := fmt.Sprintf("s3://%v/", bucket)
+	cmd := s5cmd("mv", fpath, dst)
 	result := icmd.RunCmd(cmd)
 
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`mv %v`, filename),
+		0: equals(`mv %v %v%v`, fpath, dst, filename),
 		1: equals(""),
 	})
 
@@ -158,17 +159,19 @@ func TestMoveMultipleFilesToS3(t *testing.T) {
 	workdir := fs.NewDir(t, bucket, files...)
 	defer workdir.Remove()
 
-	cmd := s5cmd("mv", workdir.Path()+"/*", "s3://"+bucket+"/")
+	src := workdir.Path()
+	dst := fmt.Sprintf("s3://%v/", bucket)
+	cmd := s5cmd("mv", src+"/*", dst)
 	result := icmd.RunCmd(cmd)
 
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`mv another_test_file.txt`),
-		2: equals(`mv filename-with-hypen.gz`),
-		3: equals(`mv readme.md`),
-		4: equals(`mv testfile1.txt`),
+		1: equals(`mv %v/another_test_file.txt %vanother_test_file.txt`, src, dst),
+		2: equals(`mv %v/filename-with-hypen.gz %vfilename-with-hypen.gz`, src, dst),
+		3: equals(`mv %v/readme.md %vreadme.md`, src, dst),
+		4: equals(`mv %v/testfile1.txt %vtestfile1.txt`, src, dst),
 	}, sortInput(true))
 
 	// expect no files on filesystem
@@ -207,7 +210,7 @@ func TestMoveSingleS3ObjectToS3(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`mv s3://%v/testfile1.txt`, bucket),
+		0: equals(`mv %v %v`, src, dst),
 		1: equals(""),
 	})
 
@@ -247,7 +250,7 @@ func TestMoveSingleS3ObjectIntoAnotherBucket(t *testing.T) {
 	result.Assert(t, icmd.Success)
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
-		0: equals(`mv s3://%v/testfile1.txt`, srcbucket),
+		0: equals(`mv %v %v`, src, dst),
 		1: equals(""),
 	})
 
@@ -290,10 +293,10 @@ func TestMoveMultipleS3ObjectsToS3(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: contains(""),
-		1: equals("mv s3://%v/another_test_file.txt", bucket),
-		2: equals("mv s3://%v/filename-with-hypen.gz", bucket),
-		3: equals("mv s3://%v/readme.md", bucket),
-		4: equals("mv s3://%v/testfile1.txt", bucket),
+		1: equals("mv s3://%v/another_test_file.txt %vanother_test_file.txt", bucket, dst),
+		2: equals("mv s3://%v/filename-with-hypen.gz %vfilename-with-hypen.gz", bucket, dst),
+		3: equals("mv s3://%v/readme.md %vreadme.md", bucket, dst),
+		4: equals("mv s3://%v/testfile1.txt %vtestfile1.txt", bucket, dst),
 	}, sortInput(true))
 
 	// expect no s3 source objects
diff --git a/e2e/rm_test.go b/e2e/rm_test.go
index d548818..958de5d 100644
--- a/e2e/rm_test.go
+++ b/e2e/rm_test.go
@@ -430,9 +430,9 @@ func TestRemoveLocalDirectory(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals("rm file1.txt"),
-		2: equals("rm file2.txt"),
-		3: equals("rm readme.md"),
+		1: equals("rm testdir/file1.txt"),
+		2: equals("rm testdir/file2.txt"),
+		3: equals("rm testdir/readme.md"),
 	}, sortInput(true))
 
 	assertLines(t, result.Stderr(), map[int]compareFunc{
@@ -472,7 +472,7 @@ func TestVariadicMultipleLocalFilesWithDirectory(t *testing.T) {
 		0: equals(""),
 		1: equals("rm file1.txt"),
 		2: equals("rm file2.txt"),
-		3: equals("rm readme.md"),
+		3: equals("rm testdir/readme.md"),
 	}, sortInput(true))
 
 	assertLines(t, result.Stderr(), map[int]compareFunc{
diff --git a/e2e/run_test.go b/e2e/run_test.go
index 997c579..f239752 100644
--- a/e2e/run_test.go
+++ b/e2e/run_test.go
@@ -210,9 +210,9 @@ func TestRunWildcardCountGreaterEqualThanWorkerCount(t *testing.T) {
 
 	assertLines(t, result.Stdout(), map[int]compareFunc{
 		0: equals(""),
-		1: equals(`cp s3://%v/file.txt`, bucket),
-		2: equals(`cp s3://%v/file.txt`, bucket),
-		3: equals(`cp s3://%v/file.txt`, bucket),
+		1: equals(`cp s3://%v/file.txt file.txt`, bucket),
+		2: equals(`cp s3://%v/file.txt file.txt`, bucket),
+		3: equals(`cp s3://%v/file.txt file.txt`, bucket),
 	}, sortInput(true))
 
 	assertLines(t, result.Stderr(), map[int]compareFunc{